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

�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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions