【问题标题】:Is it possible to use `impl Trait` as a function's return type in a trait definition?是否可以在特征定义中使用“impl Trait”作为函数的返回类型?
【发布时间】: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 的当前实现的限制还是我用错了?

【问题讨论】:

    标签: rust traits


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

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

    来自RFC 1522

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

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

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

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

    另见:

    仅每晚

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

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

      【讨论】:

        猜你喜欢
        • 2021-02-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-15
        • 1970-01-01
        相关资源
        最近更新 更多