Allow type placeholder for item signatures if they constrain the associated types


#1

I tried to write following code which seems to be disallowed by Rust:

trait Foo {
    type Foo;
    fn get_foo(&self) -> &Self::Foo;
}

/* Not allowed
trait Bar1: Foo<Foo = Option<_>> {
    fn bar(&self) -> bool {
        self.get_foo().is_some()
    }
}
*/

/* Verbose as pollutes all usage of this trait */
trait Bar2<S>: Foo<Foo = Option<S>> {
    fn bar(&self) -> bool {
        self.get_foo().is_some()
    }
}

/* Less verbose for my case but awkward and more of a workaround */
trait Bar3: Foo {
    fn bar<S>(&self) -> bool where Self: Foo<Foo = Option<S>> {
        self.get_foo().is_some()
    }
}

#2

Right, you’ve invented a nicer syntax that Rust doesn’t support.

trait Bar2<S>: Foo<Foo = Option<S>> 

This means “for each S, make a new version of this trait”, which in this case is necessary, because every Option<S> may have different layout, and a different way of encoding Some, so every call to is_some() may be different. Rust just likes to be explicit about it.


#3

Yet :wink: https://github.com/rust-lang/rust/issues/41517


#4

This means “for each S, make a new version of this trait”, which in this case is necessary, because every Option<S> may have different layout, and a different way of encoding Some, so every call to is_some() may be different. Rust just likes to be explicit about it.

I’m not sure I’m following. Since type can implement Foo only once Self::Foo needs to be known during implementation anyway. All Foo = Option<_> does is restrict what Bar2 can be implemented for. The only thing that would be different as far as I can see is that default implementation needs to be generated - but from language user perspective that’s an implementation detail.


#5

BTW, I mixed up users/internals forums and I thought your post was a question, not a proposal.

I was referring to monomorphisation. There has to be a different is_some() call for Option<&Foo> (null check) and Option<Foo> (extra boolean field). In some way it is an implementation detail, but also it’s visible in the syntax when that approach is used.


#6

Doesn’t it need to happen anyway if the trait consumes objects (fn foo(self, foo: Self)) as Self might take different amount of space? Self::Foo is dependent type so if you monomorphise in type of object implementing trait the dependent trait monomorphosation shouldn’t be much different I think.