簡體   English   中英

如何從通用 function 返回具體類型?

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

在下面的示例中, 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泛型類型聲明中返回具體類型?

如果我正確理解你的問題,你不能。 當您使用 generics 時,您讓調用者決定您的 function 必須使用的類型,因此您的函數實現本身必須是通用的。 如果您想在泛型 function 中構造並返回泛型類型,通常的 go 方法是將Default特征綁定在泛型類型上並在您的實現中使用它:

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

如果您需要有條件地 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)
    }
}

如何從“impl trait”通用類型聲明中返回具體類型?

通過“impl trait”通用類型聲明,我認為您的意思是“impl trait”重寫為使用名為 generics 然而,這是一個錯誤的前提 - impl Trait作為回報 position 被引入正是因為你不能使用命名 generics 來表達它。 要看到這一點,請考慮參數 position 中的第一個impl Trait ,例如這個 function:

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

您可以將 function 重寫為使用名為 generics 的名稱,如下所示:

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

除非有微小的技術差異,否則兩者是等價的。 但是,如果impl Trait返回 position,例如這里:

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

...您不能將其重寫為使用 generics 而不會失去一般性。 例如,這不會編譯:

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

...但是以在堆上分配迭代器和禁用一些優化為代價。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM