是否可以在特征定义中使用“impl Trait”作为函数的返回类型?

Posted

技术标签:

【中文标题】是否可以在特征定义中使用“impl Trait”作为函数的返回类型?【英文标题】:Is it possible to use `impl Trait` as a function's return type in a trait definition? 【发布时间】:2017-01-21 18:27:56 【问题描述】:

是否可以将特征内的函数定义为具有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 的当前实现的限制还是我用错了?

【问题讨论】:

【参考方案1】:

如果您只需要返回当前正在实现 trait 的特定类型,您可能正在寻找 Self

trait A 
    fn new() -> Self;

例如,这将编译:

trait A 
    fn new() -> Self;


struct Person;

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

或者,一个更完整的例子,演示使用 trait:

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

Playground

作为旁注.. 我在这里使用String 来支持&amp;str 引用.. 以减少对显式生命周期的需求,并可能失去对手头问题的关注。我相信在借用内容时返回&amp;str 引用通常是惯例,这在这里似乎很合适。但是我不想过多地分散实际示例的注意力。

【讨论】:

这与返回impl Trait 不同。例如,您不能添加在Person 中返回Pet 但在Pet 中返回Person 的方法,尽管两者都实现了A。 RFC (1522) 提到了这个限制并表示希望最终删除它(“初始限制”下的第一个项目符号)。 老实说,我没有考虑过@trentcl。我的回答似乎对 OP 有所帮助。既然被接受了,我应该怎么做? @SimonWhitehead 我建议对第一句话进行编辑(不确定同行评审过程是如何运作的,也许你可以看到)。但我认为你的答案很好,没有理由不应该被接受(毕竟它解决了 OP 的直接问题)。【参考方案2】:

作为trentcl mentions,您目前不能将impl Trait 放在特征方法的返回位置。

来自RFC 1522:

impl Trait 只能写在独立或固有实现函数的返回类型中,不能写在特征定义或任何非返回类型位置。它们也可能不会出现在闭包特征或函数指针的返回类型中,除非它们本身是合法返回类型的一部分。

最终,我们希望允许在特征中使用该功能 [...]

现在,您必须使用盒装 trait 对象:

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

另见:

Is it possible to have a constructor function in a trait? Why can a trait not construct itself? How do I return an instance of a trait from a method?

仅每晚

如果你想使用不稳定的夜间功能,你可以使用existential types (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);
    

【讨论】:

再次感谢 Shepmaster。我没有完全考虑到这一点,所以我看到我的回答并没有直接解决这个问题。也就是说,它现在已被接受,并且似乎对 OP 有所帮助。我该如何从这里开始?是否应该编辑问题以删除 impl Trait 的特定用途,还是应该努力完全删除答案? @SimonWhitehead 我不愿意如此彻底地编辑这个问题。我认为留下两个答案都很好。你可以改变你的答案,比如“虽然你还不能做 X,但这里有一个可能有帮助的解决方法”。回答直接问题并提供有用的替代方案都是有价值的贡献。复选标记主要表示“这个答案对 OP 的帮助最大”。投票的意思是“这个答案对我有帮助”。【参考方案3】:

通过使用associated type 并明确命名返回类型,即使在它没有返回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
    

【讨论】:

【参考方案4】:

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&lt;T&gt; 的类型T。这里,St1 实现了A&lt;St1&gt;,所以St2impl A&lt;St2&gt; 是可以的。但是,它不适用于例如,

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;

但我正在努力解决最后一个问题。

【讨论】:

以上是关于是否可以在特征定义中使用“impl Trait”作为函数的返回类型?的主要内容,如果未能解决你的问题,请参考以下文章

我如何让impl Trait使用适当的生命周期来对其中有另一个生命周期的值进行可变引用?

PHP 特征 - 定义通用常量

Python分类定义特征重要性

是否可以在 H2O Driverless 中定义最终模型使用多少变量

EL 空运算符如何在 JSF 中工作?

udf(用户定义函数)如何在 pyspark 中工作?