Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type annotations required - why are associated types treated differently?

Tags:

rust

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);
}
like image 688
John Avatar asked Dec 29 '25 20:12

John


2 Answers

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();
like image 66
mcarton Avatar answered Jan 01 '26 16:01

mcarton


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.

like image 34
pengowen123 Avatar answered Jan 01 '26 16:01

pengowen123