web-dev-qa-db-ja.com

トレイト定義で関数の戻り値の型として「impl Trait」を使用することは可能ですか?

トレイト内の関数を_impl Trait_戻り値型として定義することは、まったく可能ですか?複数の構造体で実装できるトレイトを作成して、それらすべてのnew()関数が、それぞれに固有のコードを記述することなく、すべて同じ方法で使用できるオブジェクトを返すようにしたい。

_trait A {
    fn new() -> impl A;
}
_

ただし、次のエラーが発生します。

_error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
 --> src/lib.rs:2:17
  |
2 |     fn new() -> impl A;
  |                 ^^^^^^
_

これは_impl Trait_の現在の実装の制限ですか、それとも間違って使用していますか?

25
Ameo

トレイトが現在実装されている特定のタイプのみを返す必要がある場合は、Selfを探している可能性があります。

trait A {
    fn new() -> Self;
}

たとえば、これはコンパイルされます:

trait A {
    fn new() -> Self;
}

struct Person;

impl A for Person {
    fn new() -> Person {
        Person
    }
}

または、トレイトの使用を示すより完全な例:

trait A {
    fn new<S: Into<String>>(name: S) -> Self;
    fn get_name(&self) -> String;
}

struct Person {
    name: String
}

impl A for Person {
    fn new<S: Into<String>>(name: S) -> Person {
        Person { name: name.into() }
    }

    fn get_name(&self) -> String {
        self.name.clone()
    }
}

struct Pet {
    name: String
}

impl A for Pet {
    fn new<S: Into<String>>(name: S) -> Pet {
        Pet { name: name.into() }
    }

    fn get_name(&self) -> String {
        self.name.clone()
    }
}

fn main() {

    let person = Person::new("Simon");
    let pet = Pet::new("Buddy");

    println!("{}'s pets name is {}", get_name(&person), get_name(&pet));
}

fn get_name<T: A>(a: &T) -> String {
    a.get_name()
}

遊び場

補足として..ここではStringを使用して&str参照..明示的なライフタイムの必要性を減らし、潜在的な目の前の問題に焦点を失う可能性を減らします。一般的には、&strコンテンツを借りるときの参照であり、ここでは適切と思われます。しかし、実際の例からあまり気を取られたくありませんでした。

12
Simon Whitehead

trentclの言及 のように、現在impl Traitトレイトメソッドの戻り位置。

から RFC 1522

impl Traitは、独立型または固有の関数の戻り型内でのみ記述できます。特性定義や非戻り型の位置では記述できません。それらが正当な戻り値の型の一部でない限り、それらはクロージャの特性または関数ポインタの戻り値の型にも表示されない場合があります。

  • 最終的には、機能をトレイト内で使用できるようにする必要があります[...]

ここでは、ボックス化された特性オブジェクトを使用する必要があります。

trait A {
    fn new() -> Box<dyn A>;
}

以下も参照してください。

毎晩のみ

不安定な夜間機能を使用したい場合は、 既存のタイプ(RFC 2071) を使用できます:

// 1.40.0-nightly (2019-11-05 1423bec54cf2db283b61)
#![feature(type_alias_impl_trait)]

trait FromTheFuture {
    type Iter: Iterator<Item = u8>;

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

impl FromTheFuture for u8 {
    type Iter = impl Iterator<Item = u8>;

    fn example(&self) -> Self::Iter {
        std::iter::repeat(*self).take(*self as usize)
    }
}

fn main() {
    for v in 7.example() {
        println!("{}", v);
    }
}
27
Shepmaster

関連付けられたタイプ を使用することにより、Selfを返さない場合でも同様の結果を得ることができます戻り値の型に明示的に名前を付ける:

trait B {}
struct C;

impl B for C {}

trait A {
    type FReturn: B;
    fn f() -> Self::FReturn;
}

struct Person;

impl A for Person {
    type FReturn = C;
    fn f() -> C {
        C
    }
}
7
Jeremy Salwen

Rustはかなり新しいので、チェックが必要な場合があります。

戻り値の型をパラメータ化できます。これには制限がありますが、単にSelfを返すよりも制限が少なくなります。

trait A<T> where T: A<T> {
    fn new() -> T;
}

// return a Self type
struct St1;
impl A<St1> for St1 {
    fn new() -> St1 { St1 }
}

// return a different type
struct St2;
impl A<St1> for St2 {
    fn new() -> St1 { St1 }
}

// won't compile as u32 doesn't implement A<u32>
struct St3;
impl A<u32> for St3 {
    fn new() -> u32 { 0 }
}

この場合の制限は、A<T>を実装するT型のみを返すことができるということです。ここでは、St1A<St1>を実装しているため、St2からimpl A<St2>までは問題ありません。ただし、たとえば、

impl A<St1> for St2 ...
impl A<St2> for St1 ...

そのためには、タイプをさらに制限する必要があります。

trait A<T, U> where U: A<T, U>, T: A<U, T> {
    fn new() -> T;
}

しかし、私はこの最後のものに頭を回すのに苦労しています。

0
joelb