Help test impl Trait!

Just reread the RFC, and realized it proposes exactly this. Is this feature still planned to implement?

Yes, however the current features planned for stabilization would disallow passing explicit type arguments altogether. This is a temporary restriction.

I’ve been reading some of the discussions around this feature (but certainly not all of them – there are many :slight_smile: ). Can someone explain to me why the impl keyword is necessary?

I think it would be much more beautiful to see

fn running_count(iter: Iterator<Item = u32>) -> Iterator<Item = u32> {
    // ...
}

To me, this seems perfectly clear: instead of a concrete type, I specify a trait which naturally bounds the types that can be used here.

That this argument position trait would mean the same as a normal generic function is unfortunate, as @theotherphil mentioned above… This kind of “redundancy” is not a desirable feature of language design.

This syntax is already taken (trait objects).

Hindsight is a lovely thing :slight_smile:

Aha... though I thought trait objects were always written as &Trait?

Well, with any alternative you’d want a way to do &impl Foo, right?

I guess &impl Foo means "return a reference to something that implements the Foo trait". That is what &Foo means today, no? I guess the difference is whether this is determined statically or dynamically?

My understanding is that a trait object is a dynamic dispatch mechanism and this impl Foo business seems to use static dispatch? I'm just a somewhat casual user of Rust who has tried to follow the discussion a little on the side-line... So perhaps take these questions as a hint that this new feature isn't super clear :slight_smile:

I find the monomorphisation done by fn foo<T: Foo>(f: T) quite simple to understand. I find it much less clear that fn foo(f: impl Foo) would have the same effect, and even less clear what it means when the trait is mentioned in the return type.

impl Trait and <T: Trait> are both statically dispatched, whilst Box<Trait>, &Trait etc are dynamically dispatched using vtables. impl Trait was introduced for existential generics (callee chooses). It was decided that it was ergonomic to allow impl Trait in argument position, and that this would be universal generics (caller chooses), like <T: Trait>.

I think that in the future trait objects will turn from Trait to dyn Trait, to make clear that they are dynamically dispatched. This is a breaking change, so it requires very careful management to not put off users interested in backwards compatibility.

Remember that this is currently unstable - not all the documentation that is required has been written, and the feature may still change. Someone new to the language should ignore this feature for now.

2 Likes

Thanks for the explanations, much appreciated!

1 Like

I wonder, why no automatic cast impl T to &T during method call?

    fn f(i: i32) -> impl Any {
        i + 1
    }

     let x = f(17);
    x.downcast_ref::<i32>()//<---- compile error

    (&x as &Any).downcast_ref::<i32>()//works

So if I understood you correctly, its currently not possible for a fn to return Result<impl Temperature, u8>, but that this is planned?

Returning Result<impl Temperature, u8> is allowed. This, on the other hand, is not:

#![feature(conservative_impl_trait, universal_impl_trait)]

struct Struct;
trait Trait {}
impl Trait for Struct {}

fn generic<T>(x: T, y: impl Trait) { }

fn main() {
    // Neither of these are allowed; both fail with:
    // error[E0632]: cannot provide explicit type parameters when
    //               `impl Trait` is used in argument position.
    generic::<u32>(3, Struct);
    generic::<u32, Struct>(3, Struct);
    
    // For it to compile, you must rely on type inference
    // and omit the turbofish entirely.
    generic(3, Struct); // ok
}

Thanks for the answer. Compiler complains about error[E0562]:impl Traitnot allowed outside of function and inherent method return types, corresponding fn signature: fn new(buf: &[u8]) -> Result<impl Temperature, u8>; Is that because its a “static” (in the Java sense of the word) method?

Is the method on a trait? If so, that's the problem. You'll get the same error even if you just returned -> impl Temperature.

Why not just return Result<Self, u8>?

Thats actually how I did it, just wanted to test impl trait. I suppose I don't yet have a good intuition what the Rust type system will let me do and what is idiomatic.

I don't think that is the issue there. This behavior makes sense given the mechanics of auto-deref and coercion; you would have the same problem writing 17i32.downcast_ref().

Any is an unusual trait, because it has no actual methods; all of its methods are exclusively defined on the Any trait object. What this means is that the downcast_ref method is not available to arbitrary &T where T: Any; you must have an &Any.

  • 3.downcast_ref() will first look for a method with an i32 receiver. Failing that, it looks for methods with &i32 or &mut i32 as the receiver, and still finds none.
  • (&3 as &Any).downcast_ref() works for obvious reasons.
  • Any::downcast_ref(&3) also works, because the &3 appears in a strongly-typed location known as a coercion site. Because it knows that an expression of type &Any must be produced, it is able to coerce &i32 into &Any, after which it can find the method.
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.