简体   繁体   English

如何从通用 function 返回具体类型?

[英]How to return concrete type from generic function?

In the example below the Default trait is used just for demonstration purposes.在下面的示例中, Default特征仅用于演示目的。

My questions are:我的问题是:

  1. What is the difference between the declarations of f() and g() ? f()g()的声明有什么区别?
  2. Why g() doesn't compile since it's identical to f() ?为什么g()无法编译,因为它与f()相同?
  3. How can I return a concrete type out of a impl trait generically typed declaration?如何从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{}
}

What is the difference between the declarations of f() and g() ? f()g()的声明有什么区别?

  • f returns some type which implements Default . f返回一些实现Default的类型。 The caller of f has no say in what type it will return . f的调用者对于它将返回什么类型没有发言权
  • g returns some type which implements Default . g返回一些实现Default的类型。 The caller of g gets to pick the exact type that must be returned . g的调用者可以选择必须返回的确切类型

You can clearly see this difference in how f and g can be called.您可以清楚地看到如何调用fg的这种差异。 For example:例如:

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()
}

Why g() doesn't compile since it's identical to f() ?为什么g()无法编译,因为它与f()相同?

They're not identical.它们并不相同。 The implementation for f compiles because it can only be called in 1 way and it will always return the exact same type. f的实现可以编译,因为它只能以一种方式调用,并且它总是返回完全相同的类型。 The implementation for g fails to compile because it can get called infinitely many ways for all different types but it will always return Something which is broken. g的实现无法编译,因为它可以被所有不同类型的无限调用方式调用,但它总是会返回损坏的Something

How can I return a concrete type out of a impl trait generically typed declaration?如何从impl trait泛型类型声明中返回具体类型?

If I'm understanding your question correctly, you can't.如果我正确理解你的问题,你不能。 When you use generics you let the caller decide the types your function must use, so your function's implementation itself must be generic.当您使用 generics 时,您让调用者决定您的 function 必须使用的类型,因此您的函数实现本身必须是通用的。 If you want to construct and return a generic type within a generic function the usual way to go about that is to put a Default trait bound on the generic type and use that within your implementation:如果您想在泛型 function 中构造并返回泛型类型,通常的 go 方法是将Default特征绑定在泛型类型上并在您的实现中使用它:

// now works!
fn g<T: Default>() -> T {
    T::default()
}

If you need to conditionally select the concrete type within the function then the only other solution is to return a trait object:如果您需要有条件地 select function 中的具体类型,那么唯一的其他解决方案是返回一个特征 object:

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)
    }
}

how can I return a concrete type out of a "impl trait" generically typed declaration?如何从“impl trait”通用类型声明中返回具体类型?

By "impl trait" generically typed declaration I presume you mean "impl trait" rewritten to use named generics .通过“impl trait”通用类型声明,我认为您的意思是“impl trait”重写为使用名为 generics However, that's a false premise - impl Trait in return position was introduced precisely because you can't express it using named generics.然而,这是一个错误的前提 - impl Trait作为回报 position 被引入正是因为你不能使用命名 generics 来表达它。 To see this, consider first impl Trait in argument position, such as this function:要看到这一点,请考虑参数 position 中的第一个impl Trait ,例如这个 function:

fn foo(iter: impl Iterator<Item = u32>) -> usize {
    iter.count()
}

You can rewrite that function to use named generics as follows:您可以将 function 重写为使用名为 generics 的名称,如下所示:

fn foo<I: Iterator<Item = u32>>(iter: I) -> usize {
    iter.count()
}

Barring minor technical differences, the two are equivalent.除非有微小的技术差异,否则两者是等价的。 However, if impl Trait is in return position, such as here:但是,如果impl Trait返回 position,例如这里:

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

...you cannot rewrite it to use generics without losing generality. ...您不能将其重写为使用 generics 而不会失去一般性。 For example, this won't compile:例如,这不会编译:

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

...because, as explained by pretzelhammer, the signature promises the caller the ability to choose which type to return (out of those that implement Iterator<Item = u32> ), but the implementation only ever returns a concrete type, <Vec<u32> as IntoIterator>::IntoIter . ...因为,正如 pretzelhammer 所解释的,签名向调用者保证了选择要返回的类型的能力(在那些实现Iterator<Item = u32>的类型中),但实现只返回一个具体类型<Vec<u32> as IntoIterator>::IntoIter

On the other hand, this does compile:另一方面,这确实编译:

fn foo() -> <Vec<u32> as IntoIterator>::IntoIter {
    vec![1, 2, 3].into_iter()
}

...but now the generality is lost because foo() must be implemented as a combination of Vec and into_iter() - even adding a map() in between the two would break it. ...但是现在失去了一般性,因为foo()必须实现为Vecinto_iter()的组合 - 即使在两者之间添加map()也会破坏它。

This also compiles:这也编译:

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

...but at the cost of allocating the iterator on the heap and disabling some optimizations. ...但是以在堆上分配迭代器和禁用一些优化为代价。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM