是否可以在特征定义中使用“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
来支持&str
引用.. 以减少对显式生命周期的需求,并可能失去对手头问题的关注。我相信在借用内容时返回&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<T>
的类型T
。这里,St1
实现了A<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;
但我正在努力解决最后一个问题。
【讨论】:
以上是关于是否可以在特征定义中使用“impl Trait”作为函数的返回类型?的主要内容,如果未能解决你的问题,请参考以下文章
我如何让impl Trait使用适当的生命周期来对其中有另一个生命周期的值进行可变引用?