关于rust:是否可以在特征定义中将“ impl Trait”用作函数的返回类型?

Is it possible to use `impl Trait` as a function's return type in a trait definition?

是否可以将特征内部的函数定义为具有impl Trait返回类型? 我想创建一个可以由多个结构实现的特征,以便所有这些结构的new()函数返回一个对象,该对象可以以相同的方式使用,而不必编写特定于每个结构的代码。

1
2
3
trait A {
    fn new() -> impl A;
}

但是,出现以下错误:

1
2
3
4
5
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当前实现的限制还是我使用错了?


如trentcl所述,您当前不能将impl Trait置于trait方法的返回位置。

根据RFC 1522:

impl Trait may only be written within the return type of a freestanding or inherent-impl function, not in trait definitions or any non-return type position. They may also not appear in the return type of closure traits or function pointers, unless these are themselves part of a legal return type.

  • Eventually, we will want to allow the feature to be used within traits [...]

现在,您必须使用一个装箱的特征对象:

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

也可以看看:

  • 是否可以在特征中具有构造函数?
  • 为什么特质不能自我构建?
  • 如何从方法返回特征的实例?

仅每晚

如果您希望使用不稳定的夜间功能,则可以使用存在类型(RFC 2071):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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);
    }
}


如果仅需要返回当前正在为其实现特征的特定类型,则可能正在寻找Self

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

例如,它将编译:

1
2
3
4
5
6
7
8
9
10
11
trait A {
    fn new() -> Self;
}

struct Person;

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

或者,举一个更完整的示例,使用该特征进行演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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引用,这似乎很合适。但是我不想过多地从实际示例中分散注意力。


通过使用关联类型并显式命名返回类型,即使在不返回Self的情况下,您也可以得到类似的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
    }
}

对Rust来说还很陌生,因此可能需要检查。

您可以对返回类型进行参数化。这是有限制的,但是它们比简单地返回Self的限制要少。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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。在这里,St1实现了A,所以St2impl A可以。但是,例如,它不适用于

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

为此,您需要进一步限制类型,例如

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

但我在努力争取最后一个。