fn foo<T: Display>(x: T) -> T {
return x;
}
fn foo2(x: impl Display) -> impl Display {
return x;
}
Can anyone tell me the difference? thanks a lot.
fn foo<T: Display>(x: T) -> T {
return x;
}
fn foo2(x: impl Display) -> impl Display {
return x;
}
Can anyone tell me the difference? thanks a lot.
There’s no meaningful difference in how the arguments acts, insofar as I’m aware.
Callers know what the return type of foo
is, so they can do more than just display it.
Callers have no idea what the return type of foo2
is, so they can’t do anything other than just display it.
Thanks a lot. I wrote a code snippet to show what I’ve found
fn foo<T: Display>(x: T) -> T {
return x;
}
fn foo2(x: impl Display) -> impl Display {
return x;
}
fn main() {
let x = "abc";
let xx : &str = foo(x);
let xx2 : &str = foo2(x); // compile error: expected &str, found anonymized type
let xx3 : impl Display = foo2(x); // compile error: `impl Trait` not allowed outside of function and inherent method return types
}
Right.
With just look at the type of function foo
, you can conclude:
Display
.Display
contains only one method, namely fmt
, and it takes self
by reference, you can conclude this is functionaly equivalent tofn foo<T: Display>(x: &T){...}
except that in the usage site it is slightly different.
On the other hand the signature of foo2
tells the same thing of its parameter, but for the return type, it only tells you it is also a Display
but you have no idea what it is. So it can return you any value that Display
and just use the input parameter in a println!
macro or similar.
Examples. You can write
fn foo2(x: impl Display) -> impl Display {
println!("{}",x);
100
}
but the closest thing you can do for foo
is something like
fn foo<T: Display>(x: T) -> T {
println!("{}", x);
x
}
in other words, you are not able to return anything other than x
(unless unsafe
ly, of cause). More interestingly, although you can say fn foo<T:Display>(mut x: T) -> T {...}
, you will not be able to mutate x
as Display
does not contain any mutable method.
Thank you very much for you detailed explaination.
It seems that I have another question.
I found that "// compile error:
impl Trait not allowed outside of function and inherent method return types"
is quite confusing.
What is the officaly way to store the return value of impl Trait
into a struct?
I found this: How do you store the result of an `impl Trait` return type? - help - The Rust Programming Language Forum
But it is way more complex than I expected
Is impl Trait
not supposed to be store in the struct at all?
That link is about storing it in a struct or enum. In your example you can just do let xx2 = foo2(x)
.
I also want to store the value into struct
/enum
.
I have C++/Java backgound, so I do expect things can be easily store in the local variable and also in struct
/enum
.
So:
Just as a general note: Rust is not C++, nor is it Java. Approaching it in terms of "design for C++/Java, then translate to Rust" is going to cause you problems.
Anyway, impl Trait
isn't fully implemented yet. There are cases where it simply is not going to work because of that, and there's not much you can do other than wait.
In the case of using it in struct
s, I'm not sure that's on the cards. At least, not directly. The closest is probably going to be defining a named, existential type in the module and using that as both a struct
field, and the return type from the function.
Until then, you're probably going to have to use either boxing, or generics.
I also want to store the value into struct/enum. I have C++/Java backgound, so I do expect things can be easily store in the local variable and also in struct /enum.
The point of impl Display
in return position is, that the caller
doesn't know the exact type, but only knows that it implements Display
.
To put the impl Display
into a struct you've to box it:
let x = Box::new(foo2(1));
Sure, I certainly don’t expect Rust be exactly like Java/C++ and I love to learn to approache the problem in Rust way, that’s why I asked “philosophy” question. ;D Thanks for your answers .
Thank you very much. That is very convenient. To summarize, here is the code snippet:
fn foo<T: Display>(x: T) -> T {
return x;
}
fn foo2(x: impl Display) -> impl Display {
return x;
}
fn main() {
let x = "abc";
let xx : &str = foo(x);
// let xx2 : &str = foo2(x); // compile error: expected &str, found anonymized type
// let xx3 : impl Display = foo2(x); // compile error: `impl Trait` not allowed outside of function and inherent method return types
let xx4 : Box<Display> = Box::new(foo2(x)); // success!
println!("{}", xx4); // success!
}
-> T
means the caller chooses the actual type, known to the caller, unknown in the function.-> impl Tr
means the function chooses actual type, known to the function, unknown to the caller.OTOH:
fn<T: Tr>(_: T)
fn(_: impl Tr)
are identical.
This might be helpful: https://rust-lang-nursery.github.io/edition-guide/2018/transitioning/traits/impl-trait.html
Basically, it's just not done yet. RFC 2071 proposed adding a variant of impl Trait
where you give the type an explicit name; it was accepted a whole eight months ago, but nobody has actually implemented it.
(On the other hand, if you use the existing impl Trait
syntax rather than this proposed variant, I don't think there's any consensus on whether there should ever be a way to name that type, e.g. a typeof
operator.)
For what it's worth, C++ has a similar problem with lambdas, which similarly have an unnameable type and cannot be stored in a struct without making the struct generic.
I haven’t dug very deep yet but isn’t there also a difference in terms of monomorphisation
with the plain type and dynamic dispatch
with the impl Trait
?
impl Trait
never adds dynamic dispatch. In return position it hides the type from the caller only in the sense that the compiler will complain if the caller tries to depend on it, not in the sense that any runtime indirection is introduced. In argument position it’s just sugar for existing generics, so same monomorphization rules.
Thank you !
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.