#![allow(dead_code)]
struct Sheep {}
trait Animal {
fn new() -> Self ;
fn noise(&self) -> &'static str;
}
impl Animal for Sheep {
fn new() -> Self{
Sheep{}
}
fn noise(&self) -> &'static str {
"xxxxxxxxxxxxxx"
}
}
fn f1() -> impl Animal {
Sheep {}
}
fn f2<T: Animal>(t: T) -> T {
t
}
fn f3<T: Animal>() -> T {
T::new()
}
fn f4<T: Animal>() -> T {
Sheep{}
}
f4
函数会报:
mismatched types
expected type parameter `T`
这个要怎么解释,为什么f1
,f2
,f3
可以,f4
就不行了呢
补充:
可能原因是:返回的值是实现了某个trait的对象,所以生成这个对象也得是通过trait的方法生成。
希望有智慧的大神可以指点一下~~
转过弯了,谢谢大家~~
1
共 6 条评论, 1 页
评论区
写评论要理解这个问题首先要明确的是rust的泛型是会单态化的,也就是
fn f4<T: Animal>() -> T
类似的签名最终会被所有实现Animal的进行展开:fn f4<Sheep>() -> Sheep
、fn f4<Cattle>() -> Cattle
... 显然你在f4
中只返回Sheep
肯定不行。那么为什么
fn f1() -> impl Animal
可以呢,关于这个问题我们就要充分理解impl trait
的使用了,其实impl trait
和泛型还是有很多区别的。首先我来看下edition-guide
中的impl Trait for returning complex types with ease,然后总结出impl trait
使用使用上的特别之处:首先,
impl trait
可以用于参数位置和返回值位置。其次,
impl trait
用于参数位置的时候和使用泛型在功能上是很接近的,比如:但是区别是这里要特别注意使用
impl trait
时无法使用turbo-fish
如:foo::<usize>(1)
!其次,在返回位置时两者有明显的区别。考虑如下代码:
新版本:
但是在使用
impl trait
时:总结一下也就是说第一点,使用泛型返回时,具体的类型可由调用者来决定,而
impl trait
返回时其实可由函数本身来决定;第二点,泛型实际上是any
的语义,而impl trait
实际上是some
的语义。附:有关
impl trait
的设计可以查阅rfcs
中的内容expand impl trait。文中在背景中有对any
和some
的说明,我摘录了一点:你别把dyn trait和impl trait搞混了,前者是引用一个类型编译时未知,但是实现了该trait的对象,而后者则是编译的时候类型固定且编译时必须已知的,仅仅是一个约束而已,两者完全不同。
f4你约束了参数和返回值都是T类型,而T的具体类型是在调用这个函数的时候从参数或者函数的泛型参数推导得出的,而你的返回值却在没使用的时候就固定为了Sheep类型,这会造成类型不匹配。
泛型其实就是一个代码生成器,你的f4在使用的时候会生成一个具体的函数,例如你这么调用f4
就会生成一个类似这样的函数
咋一看没啥问题,但是如果你这么用
自然就会生成一个类似这样的函数
这样很明显就是错的,返回值类型与返回的类型不匹配。
如果你要返回一个Sheep类型,且参数也想用trait约束,那就用dyn trait,这个是可以的,类似java的interface。比如这样
这种写法,上面有写,不过想知道
f4
为毛不行,特别是和f1
对比起来看--
👇
ljjava: fn f4<T: Animal>() -> T { T::new() }
报错是
f4
这个函数有语法错误。--
👇
gwy15: 你要返回的是个泛型参数
T: Animal
,但是你直接返回了Sheep
,如果这样写不就类型不匹配了吗
fn f4<T: Animal>() -> T { T::new() }
你要返回的是个泛型参数
T: Animal
,但是你直接返回了Sheep
,如果这样写不就类型不匹配了吗