簡體   English   中英

如何在關聯類型中指定生命周期參數?

[英]How do I specify lifetime parameters in an associated type?

我有這個特點和簡單的結構:

use std::path::{Path, PathBuf};

trait Foo {
    type Item: AsRef<Path>;
    type Iter: Iterator<Item = Self::Item>;

    fn get(&self) -> Self::Iter;
}

struct Bar {
    v: Vec<PathBuf>,
}

我想為Bar實現Foo特性:

impl Foo for Bar {
    type Item = PathBuf;
    type Iter = std::slice::Iter<PathBuf>;

    fn get(&self) -> Self::Iter {
        self.v.iter()
    }
}

但是我收到此錯誤:

error[E0106]: missing lifetime specifier
  --> src/main.rs:16:17
   |
16 |     type Iter = std::slice::Iter<PathBuf>;
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ expected lifetime parameter

我發現無法在該關聯類型中指定生命周期。 我特別想表達的是迭代器的生命周期不能超過self生命周期。

我必須如何修改Foo特征或Bar特征實現才能使其工作?

鐵銹游樂場

您的問題有兩種解決方案。 讓我們從最簡單的開始:

為您的特質增添一生

trait Foo<'a> {
    type Item: AsRef<Path>;
    type Iter: Iterator<Item = Self::Item>;
    
    fn get(&'a self) -> Self::Iter;
}

這要求您在使用該特征的任何地方注釋生命周期。 當你實現 trait 時,你需要做一個通用的實現:

impl<'a> Foo<'a> for Bar {
    type Item = &'a PathBuf;
    type Iter = std::slice::Iter<'a, PathBuf>;
    
    fn get(&'a self) -> Self::Iter {
        self.v.iter()
    }
}

當你需要一個泛型參數的 trait 時,你還需要確保對你的 trait 對象的任何引用都具有相同的生命周期:

fn fooget<'a, T: Foo<'a>>(foo: &'a T) {}

實現 trait 以引用您的類型

不要為您的類型實現 trait,而是為您的類型引用實現它。 trait 永遠不需要以這種方式了解有關生命周期的任何信息。

然后特征函數必須按值獲取其參數。 在您的情況下,您將實現 trait 以供參考:

trait Foo {
    type Item: AsRef<Path>;
    type Iter: Iterator<Item = Self::Item>;
    
    fn get(self) -> Self::Iter;
}

impl<'a> Foo for &'a Bar {
    type Item = &'a PathBuf;
    type Iter = std::slice::Iter<'a, PathBuf>;
    
    fn get(self) -> Self::Iter {
        self.v.iter()
    }
}

你的fooget函數現在簡單地變成

fn fooget<T: Foo>(foo: T) {}

問題在於fooget函數不知道T實際上是&Bar 當您調用get函數時,您實際上是在移出foo變量。 您不會移出對象,而只是移動參考。 如果您的fooget函數嘗試調用get兩次,則該函數將無法編譯。

如果您希望fooget函數僅接受為引用實現了Foo特征的參數,則需要明確聲明此界限:

fn fooget_twice<'a, T>(foo: &'a T)
where
    &'a T: Foo,
{}

where子句確保您只為引用調用此函數,其中為引用而不是類型實現了Foo 也可以對兩者實施。

從技術上講,編譯器可以自動推斷fooget_twice的生命周期,因此您可以將其寫為

fn fooget_twice<T>(foo: &T)
where
    &T: Foo,
{}

但它不是足夠聰明尚未


對於更復雜的情況,您可以使用尚未實現的 Rust 功能: 通用關聯類型(GAT)。 相關工作在issue 44265 中進行跟蹤。

使用包裝類型

如果 trait 及其所有實現都定義在一個 crate 中,則輔助類型可能很有用:

trait Foo {
    fn get<'a>(&'a self) -> IterableFoo<'a, Self> {
        IterableFoo(self)
    }
}

struct IterableFoo<'a, T: ?Sized + Foo>(pub &'a T);

對於實現Foo的具體類型,在包裝它的IterableFoo上實現迭代器轉換:

impl Foo for Bar {}

impl<'a> IntoIterator for IterableFoo<'a, Bar> {
    type Item = &'a PathBuf;
    type IntoIter = std::slice::Iter<'a, PathBuf>;
    fn into_iter(self) -> Self::IntoIter {
        self.0.v.iter()
    }
}

此解決方案不允許在不同的板條箱中實現。 另一個缺點是IntoIterator綁定無法編碼到特征的定義中,因此需要將其指定為想要迭代Foo::get結果的通用代碼的附加(和更高等級)綁定:

fn use_foo_get<T>(foo: &T)
where
    T: Foo,
    for<'a> IterableFoo<'a, T>: IntoIterator,
    for<'a> <IterableFoo<'a, T> as IntoIterator>::Item: AsRef<Path>
{
    for p in foo.get() {
        println!("{}", p.as_ref().to_string_lossy());
    }
}

提供所需功能的內部對象的關聯類型

trait 可以定義一個關聯類型,該類型可以訪問綁定在引用中的對象的一部分,提供必要的訪問 trait。

trait Foo {
    type Iterable: ?Sized;

    fn get(&self) -> &Self::Iterable;
}

這要求任何實現類型都包含可以如此公開的部分:

impl Foo for Bar {
    type Iterable = [PathBuf];

    fn get(&self) -> &Self::Iterable {
        &self.v
    }
}

在使用get結果的泛型代碼中對關聯類型的引用設置邊界:

fn use_foo_get<'a, T>(foo: &'a T)
where
    T: Foo,
    &'a T::Iterable: IntoIterator,
    <&'a T::Iterable as IntoIterator>::Item: AsRef<Path>
{
    for p in foo.get() {
        println!("{}", p.as_ref().to_string_lossy());
    }
}

此解決方案允許在 trait 定義包之外的實現。 通用網站上的綁定工作與之前的解決方案一樣令人討厭。 實現類型可能需要一個內部 shell 結構,其唯一目的是提供關聯類型,以防使用站點邊界不像討論的示例中的VecIntoIterator那樣容易滿足。

將來,您將需要一個關聯的類型構造函數,用於您的生命周期'a但 Rust 尚不支持。 請參閱RFC 1598

暫無
暫無

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

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