Why "impl Trait" and "T: Trait" are different?

I read the topic, and I think that they are the same.
https://doc.rust-lang.org/book/ch10-02-traits.html#trait-bound-syntax

But it seems different?
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b12984223741cef22a54647aed6405e1

fn trait_fn<T: Copy>(a: T) -> T {
    // err: Display is not implemented
    // println!("{}", a);
    a
}

fn trait_iml(a: impl Copy) -> impl Copy {
    // err: Display is not implemented
    // println!("{}", a);
    a
}

fn trait_where<T>(a: T) -> T
where
    T: Copy,
{
    // err: Display is not implemented
    // println!("{}", a);
    a
}

fn main() {
    println!("{}", trait_fn(1));

    // err: Display is not implemented
    // Why would the other two be OK?
    // println!("{}", trait_iml(1));

    // behavior like trait_fn
    println!("{}", trait_where(1));
}

They are the same only in function argument types, not in return types. Further down in the document you linked it talks about return types. Using impl Trait instead of T: Trait without changing the meaning of the signature is only possible if the T is only used once, in (a: T) -> T it is used twice.

The signature

fn trait_iml(a: impl Copy) -> impl Copy 

is only equivalent to

fn trait_iml<T: Copy>(a: T) -> impl Copy 

the “opaque” return type is still there.

4 Likes

Thanks for your reply. Did you mean this topic?
https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits
Sorry, I could not find any information about that you talk. Could you point out where it is, please?

In addtion, why does the variable 'a' could be printed in main function, but could not be printed in trait_fn function?
I think the both have the same type <T: Copy>, so they could not be printed.

fn trait_fn<T: Copy>(a: T) -> T {
    // err: Display is not implemented
    // println!("{}", a);
    a
}
fn main() {
    println!("{}", trait_fn(1));
}

Main knows that T is the type i32, which implements Display. On the other hand, as far as trait_fn is concerned, T might be any type that the caller chooses which implements Copy, so it can only do things that are valid for all types that implement Copy.

1 Like

yup


By the way, I did not say anything about why the println doesn’t work inside of the trait_… functions, my main point was to point out why there is a difference between trait_fn and trait_iml even though you read somewhere that using impl Trait is “the same” as using T: Trait. By the way, trait_where is the same as trait_fn.

The feature is not too extensively documented. If you want to read more details besides what’s in the book, maybe the (first) RFC for the feature does some more explaining. (Note that that RFC was created before impl Trait in argument position existed, so there will be no comparison/contrasting to that feature.)

1 Like

Thanks a lot.
This is my understanding, if there is something wrong, please, help to correct it

The following both are the same.

fn trait_fn<T: Copy>(a: T) -> T
fn trait_where<T>(a: T) -> T
where
    T: Copy,

And because of monomorphization, after compiling, in Main they could be

fn trait_fn_i32(a: i32) -> i32
fn trait_where_i32(a: i32) -> i32

So Main knows that T is the type i32 , which implements Display.
But in trait_fn, variable "a" may be other types which do not implement Display.

And The following both are the same, but the return type is explicit impl Copy, so println! failed

fn trait_iml(a: impl Copy) -> impl Copy 
fn trait_iml<T: Copy>(a: T) -> impl Copy