Skip to content

If traits-as-types no longer implicitly passed-by-reference, then rustc should reject passing them by value #5088

Closed
@pnkfelix

Description

@pnkfelix
Member

�If traits no longer implicitly passed-by-reference, then rustc should reject passing traits by value.

My understanding from #3635 is that traits were once implicitly boxed with @, but now you must explicitly include an pointer-type annotation when you use a trait as an object type.

I illustrate some of the cases that arise in the code sample below; the comments indicate how I think each case should be handled.

The heart of this ticket is that I am suggesting that rustc be changed so that fromV below would be rejected, as it does not make sense to pass ByVal by-value, since e.g. it is a trait and therefore we do not know what the underlying object-size is. (Alternatively, rustc could treat it as an implicitly polymorphic function, analogous to fromT in the example. But I think that would be a mistake -- if the user wants polymorphism, they should the syntax for it.)

trait ByRef         { fn ref_to_int(&self) -> int; }

trait ByVal         { fn val_to_int(self) -> int; }

// Ideally this would carry a default implementation that DELegates,
// but default methods are experimental and signal error from rustc.
trait ByDel : ByRef { fn del_to_int(self) -> int; }

impl ByRef for int  { fn ref_to_int(&self) -> int { 2 } }

impl ByVal for int  { fn val_to_int( self) -> int { 3 } }

impl ByDel for int {  fn del_to_int(self) -> int { self.ref_to_int() } }

// One can do this (passing trait-by-reference == object type).  Good.
fn fromH(_a:@ByRef) -> int {
    if false { _a.ref_to_int() } else { 5 };
    5
}

// Probably should *not* be able even to declare this function
// (passing trait by value is senseless).  It does compile currently;
// presumably this is compiling into implicit reference.):
fn fromV(_a:ByVal) -> int {
    // if false { _a.val_to_int() } else { 6 }; // (leave in comment, senseless)
    6
}

// But one *should* be able to do this (passing concrete T by value is sane):
fn fromT<T:ByVal>(_a: T) -> int {
    // Unfortunately, if you uncomment this, you hit ICE #4406.
    // if false { _a.val_to_int() } else { 7 };
    7
}

// This is similar to fromT above; it is just meant to add a check of
// pass-self-by-value traits that extend object-compatible traits.
// Unfortunately, like fromT above, it hits same ICE, so cannot test.
fn fromD<T:ByDel>(_a: T) -> int {
    // Unfortunately, if you uncomment this, you hit ICE #4406.
    // if false { _a.del_to_int() } else { 8 };
    8
}

fn main() {
    io::println(fmt!("%?", fromH(@10 as @ByRef)));
    io::println(fmt!("%?", fromV(10 as ByVal)));
    io::println(fmt!("%?", fromT(10)));
    io::println(fmt!("%?", fromD(10)));
}

The ByDel example is my attempt to implement a suggestion that Niko gave me, where he was suggesting dividing a trait carrying both object-compatible and object-incompatible methods (see #5086) into two traits, where the object-incompatible trait would inherit from the object-compatible one. I see now that #5086 is closed; I do not yet know what that implies for the state of Niko's suggestion.

Activity

pnkfelix

pnkfelix commented on Feb 22, 2013

@pnkfelix
MemberAuthor

see also #4406

pnkfelix

pnkfelix commented on Feb 22, 2013

@pnkfelix
MemberAuthor

I guess we also need to reject polymorphic-instantiations like fromT::<ByDel>, since we cannot pass ByDel by-value any more than we can pass ByVal by-value. That scenario was not explicitly explored above.

nikomatsakis

nikomatsakis commented on Feb 22, 2013

@nikomatsakis
Contributor

I'm not quite sure what you were going for here... it's not quite what I meant to suggest, anyhow. I meant to suggest that if you have:

trait Both {
    fn foo(&self);
    fn bar(self);
}

Then under the rules I proposed (and later retracted) in #5086, it would be illegal to have an object type @Both, because we can't compile handle the by-value bar() method since we don't know the type of the receiver. Under today's rules, it would be legal to have the trait type @Both but @Both is not considered to implement the trait Both.

Now, in either scenario, suppose I wanted to have a function like

fn do_something<B: Both>(b: &B) { ... b.foo() ... }

Here the point is that it only calls foo() and not bar(), so in principle it should be fine to use an object type for B.

In that case, I can refactor my trait Both as follows:

trait Foo { fn foo(&self); }
trait Both : Foo { fn bar(self); }

and I can rewrite do_something() as:

fn do_something<F: Foo>(f: &F) { ... f.foo() ... }

This will work fine.

Of course, as I proposed in #5087, I'd like to say that no object-type @T implements the trait T by default. But this kind of refactoring may still be relevant. After all, it is possible to write:

impl Foo for @Foo {
    fn foo(&self) { self.foo(); }
}

but it is not legal to write

impl Both for @Foo {
    fn foo(&self) { self.foo(); }
    fn bar(self) { self.bar(); } // illegal: cannot invoke by-val-self method on object
}
pnkfelix

pnkfelix commented on Feb 22, 2013

@pnkfelix
MemberAuthor

Just to clarify my motivation to Niko: ByRef in my code is meant to be analogous to your Foo, and ByDel is analogous to your Both ; that is, the trait ByDel is an extension of the ByRef trait.

(The rest of the code surrounding this was either

  1. me exploring the space of possible expressions, or
  2. my attempting to dig into a scenario for why one would ever want a by-value trait to extend an object-compatible trait. In particular, the scenario I was intending to illustrate is that the default implementation of ByDel would use the implementation provided by ByRef, by invoking it. But I could not test whether this works, due to the aformentioned ICE.)
catamorphism

catamorphism commented on Apr 25, 2013

@catamorphism
Contributor

Seems non-critical for 0.7. Nominating for milestone 2 (well-defined).

graydon

graydon commented on May 2, 2013

@graydon
Contributor

trait-as-type with no sigil is an error; this should be fixed. declining nomination as we think it's fixed.

added 2 commits that reference this issue on May 2, 2020

Auto merge of rust-lang#5088 - rust-lang:gha, r=Manishearth,flip1995

Auto merge of rust-lang#5190 - flip1995:nuke_traveyor, r=phansch,Mani…

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @graydon@nikomatsakis@pnkfelix@catamorphism

        Issue actions

          If traits-as-types no longer implicitly passed-by-reference, then rustc should reject passing them by value · Issue #5088 · rust-lang/rust