Why do I need this extra type annotation?


#1

Hi rustaceans, I am a rust beginner. I successfully wrote this little server that can serve a resized JPG.

But I’m curious why I needed to introduce a type annotation that feels like it should not have been necessary. If I try to skip the declaration of b and use thumbnail directly instead, I get:

src/main.rs:62:12: 62:26 error: the trait `modifier::Modifier<iron::response::Response>` is not implemented for the type `Box<MyImage>` [E0277]
src/main.rs:62         Ok(Response::with((content_type.clone(), status::Ok, thumbnail)))
                          ^~~~~~~~~~~~~~

#2

Let’s examine the return type:

fn make_thumbnail(original: image::DynamicImage, size: u32) -> Box<MyImage> {

So this returns a Box<MyImage>. Your annotation:

let b : Box<WriteBody + Send> = thumbnail;

It’s different! So this isn’t so much an annotation as it is a cast. You’re casting from a concrete type to a trait object.


#3

Thanks @steveklabnik!

What I think I just learned is that

fn thing(arg: SomeTrait)

is not synonymous with

fn thing<T>(arg: T) where T: SomeTrait

The first demands an actual trait object, the second will accept any concrete type that implements the trait.


#4

Yup! You’ll commonly see the first as

fn thing(arg: &SomeTrait)

#5

The other thing that was throwing me is the semantics of implementing a trait for another trait. I had an incorrect assumption that this would work:

struct MyConcrete;
trait MyTrait {}
impl MyTrait for MyConcrete {}    
trait OtherTrait {}
impl OtherTrait for MyTrait {}

fn foo<T : OtherTrait>(_ : T) {
}

fn main() {
    let elt = MyConcrete;
    foo(elt) // the trait `OtherTrait` is not implemented for the type `MyConcrete`
}

#6

You can make a generic implementation of a trait for a type that implements another like so:
impl<T> OtherTrait for T where T: MyTrait {}