web-dev-qa-db-ja.com

関連付けられたタイプでライフタイムパラメータを指定するにはどうすればよいですか?

私はこの特性と単純な構造を持っています:

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

FooBar特性を実装したい:

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トレイトの実装をどのように変更する必要がありますか?

錆の遊び場

30
mbrt

問題には2つの解決策があります。最も簡単なものから始めましょう:

あなたの特性に一生を加える

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

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

これには、特性を使用するすべての場所で有効期間に注釈を付ける必要があります。トレイトを実装するときは、一般的な実装を行う必要があります。

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

ジェネリック引数にトレイトが必要な場合は、トレイトオブジェクトへの参照が同じ存続期間を持つようにする必要もあります。

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

タイプへの参照の特性を実装する

タイプに特性を実装する代わりに、タイプへの参照に実装します。トレイトは、この方法でライフタイムについて何も知る必要はありません。

その場合、トレイト関数は引数を値で受け取る必要があります。あなたのケースでは、参照の特性を実装します:

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を2回呼び出そうとすると、関数はコンパイルされません。

fooget関数がFooトレイトが参照用に実装されている引数のみを受け入れるようにする場合は、この境界を明示的に指定する必要があります。

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

where句を使用すると、型ではなく参照にFooが実装されている参照に対してのみ、この関数を呼び出すことができます。両方に実装することもできます。

技術的には、コンパイラはfooget_twiceの寿命を自動的に推測できるため、次のように記述できます

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

しかし、それは十分スマートではありません まだ


より複雑なケースでは、Rustまだ実装されていない機能を使用できます: Generic Associated Types (GATs)。その作業は追跡されています 問題44265

26
oli_obk

ラッパータイプを使用する

トレイトとそのすべての実装が1つのクレートで定義されている場合、ヘルパータイプが役立ちます。

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

このソリューションでは、別のクレートでの実装は許可されません。もう1つの欠点は、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 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());
    }
}

このソリューションは、特性定義クレートの外での実装を許可します。一般的な使用サイトでの拘束された作業は、前のソリューションと同じくらい厄介です。実装型には、使用サイトの境界が例のVecおよびIntoIteratorほど容易に満たされない場合に、関連する型を提供するという唯一の目的を持つ内部Shell構造体が必要になる場合があります。議論した。

1
mzabaluev

将来的には、 wantに関連付けられた型コンストラクター が一生あります'aしかしRustはまだサポートしていません。 RFC 1598 を参照してください)

1
Jeff Burdges