如何从泛型函数返回具体类型?



在下面的例子中,Default性状仅用于演示目的。

我的问题是:

  1. f()g()的申报有什么区别?
  2. 为什么g()不编译,因为它与f()相同?
  3. 我如何从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的调用者可以选择必须返回的确切类型

您可以清楚地看到fg的调用方式的差异。例如:

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()必须作为Vecinto_iter()的组合来实现-即使在两者之间添加map()也会破坏它。

这也编译:

fn foo() -> Box<dyn Iterator<Item = u32>> {
Box::new(vec![1, 2, 3].into_iter())
}

…但代价是在堆上分配迭代器并禁用一些优化。

最新更新