Skip to content

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

@pnkfelix

Description

@pnkfelix

�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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions