在下面的例子中,Default
性状仅用于演示目的。
我的问题是:
f()
和g()
的申报有什么区别?- 为什么
g()
不编译,因为它与f()
相同? - 我如何从
impl trait
泛型声明中返回具体类型?
struct Something {
}
impl Default for Something {
fn default() -> Self {
Something{}
}
}
// This compiles.
pub fn f() -> impl Default {
Something{}
}
// This doesn't.
pub fn g<T: Default>() -> T {
Something{}
}
f()
和g()
的声明有什么区别?
f
返回实现Default
的某种类型。f
的调用者无法决定返回的类型。g
返回实现Default
的某种类型。g
的调用者可以选择必须返回的确切类型。
您可以清楚地看到f
和g
的调用方式的差异。例如:
fn main() {
let t = f(); // this is the only way to call f()
let t = g::<i32>(); // I can call g() like this
let t = g::<String>(); // or like this
let t = g::<Vec<Box<u8>>(); // or like this... and so on!
// there's potentially infinitely many ways I can call g()
// and yet there is only 1 way I can call f()
}
为什么
g()
不能编译,因为它与f()
相同?
它们不完全相同。f
的实现可以编译,因为它只能以一种方式调用,并且它将始终返回完全相同的类型。g
的实现无法编译,因为它可以以无限多种方式调用所有不同的类型,但它总是返回Something
,这是坏的。
如果我对你的问题理解正确的话,你不能。当使用泛型时,您让调用者决定函数必须使用的类型,因此函数的实现本身必须是泛型的。如果你想在泛型函数中构造并返回一个泛型类型,通常的方法是在泛型类型上设置一个如何从
impl trait
泛型声明中返回具体类型?
Default
特征绑定,并在实现中使用它:
// now works!
fn g<T: Default>() -> T {
T::default()
}
如果你需要有条件地选择函数中的具体类型,那么唯一的解决方案是返回一个trait对象:
struct Something;
struct SomethingElse;
trait Trait {}
impl Trait for Something {}
impl Trait for SomethingElse {}
fn g(some_condition: bool) -> Box<dyn Trait> {
if some_condition {
Box::new(Something)
} else {
Box::new(SomethingElse)
}
}
如何从" impl trait "返回具体类型?泛型声明?
Byimpl trait"泛型声明我想你的意思是"隐含特征";重写为使用命名泛型。然而,这是一个错误的前提—在返回位置引入impl Trait
正是因为您不能使用命名泛型表示它。要了解这一点,首先考虑参数位置上的impl Trait
,例如下面的函数:
fn foo(iter: impl Iterator<Item = u32>) -> usize {
iter.count()
}
你可以用命名泛型重写这个函数,如下所示:
fn foo<I: Iterator<Item = u32>>(iter: I) -> usize {
iter.count()
}
除微小的技术差异外,两者是等效的。但是,如果impl Trait
处于返回位置,例如:
fn foo() -> impl Iterator<Item = u32> {
vec![1, 2, 3].into_iter()
}
…你不能重写它来使用泛型而不失去一般性。例如,这不会编译:
fn foo<T: Iterator<Item = u32>>() -> T {
vec![1, 2, 3].into_iter()
}
…因为,正如pretzelhammer解释的那样,签名承诺调用者能够选择返回哪种类型(从那些实现Iterator<Item = u32>
的类型中),但实现只返回一个具体类型<Vec<u32> as IntoIterator>::IntoIter
。
另一方面,这确实编译:
fn foo() -> <Vec<u32> as IntoIterator>::IntoIter {
vec![1, 2, 3].into_iter()
}
…但现在的通用性失去了,因为foo()
必须作为Vec
和into_iter()
的组合来实现-即使在两者之间添加map()
也会破坏它。
这也编译:
fn foo() -> Box<dyn Iterator<Item = u32>> {
Box::new(vec![1, 2, 3].into_iter())
}
…但代价是在堆上分配迭代器并禁用一些优化。