John
John

Reputation: 2008

Type annotations required - why are associated types treated differently?

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

Answers (2)

pengowen123
pengowen123

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

mcarton
mcarton

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

Related Questions