Reputation: 2008
In the following program (play), the FooBar
trait provides the bar
method, but the actual type of the object returned by bar
seems to be hidden. If I use a type argument instead of an associated type, it works (play).
Why are associated types treated differently? Or am I doing it wrong?
use std::ops::DerefMut;
pub trait FooBar: Sized {
type Assoc: Sized + DerefMut<Target=Self>;
fn foo(&mut self) -> Option<Self::Assoc>;
fn bar(mut this: Self::Assoc) -> Result<Self::Assoc, Self::Assoc> {
unimplemented!()
}
}
#[derive(Debug)]
struct Test(u32);
impl FooBar for Test {
type Assoc = Box<Test>;
fn foo(&mut self) -> Option<Self::Assoc> {
unimplemented!()
}
}
fn main() {
let mut tt = Test(20);
let tt_foo: Box<Test> = tt.foo().unwrap(); // this is ok
let tt_bar: Box<Test> = FooBar::bar(Box::new(tt)).unwrap(); // but not this
assert_eq!(tt_bar.0, 20);
}
Upvotes: 3
Views: 1082
Reputation: 1007
The problem is you are trying to access the associated type from the trait. You can only access it from a type that implements the trait, such as from Test
:
let tt_bar: Box<Test> = Test::bar(Box::new(tt)).unwrap();
FooBar::Assoc
is not a concrete type, so you cannot use it. When you implemented FooBar
for Test
, you gave Test::Assoc
a concrete type, which is accessible:
type Assoc = Box<Test>;
In the code with the generic type, a new copy of FooBar::bar
was created with a concrete type. Because you requested a Box<Test>
, the new function's signature would be this:
fn bar(mut this: Box<Test>) -> Result<Box<Test>, Box<Test>>
Box<Test>
is a concrete type, so it works.
Upvotes: 1
Reputation: 29981
If your method is
fn bar(mut this: Self::Assoc) -> Result<Self::Assoc, Self::Assoc>
and you try to call it with
FooBar::bar(Box::new(tt))
how is Rust supposed to know what type Self
is? Box::new(tt)
is Self::Assoc
right, but you can’t get Self
from that, several types could have the same Assoc
.
And that’s what rustc
is complaining about:
type annotations required
You’d have to annotate what type Self
is:
let tt_bar: Box<Test> = <Test as FooBar>::bar(Box::new(tt)).unwrap();
or equivalently:
let tt_bar: Box<Test> = Test::bar(Box::new(tt)).unwrap();
Upvotes: 4