トレイト内の関数を_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
_の現在の実装の制限ですか、それとも間違って使用していますか?
トレイトが現在実装されている特定のタイプのみを返す必要がある場合は、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
コンテンツを借りるときの参照であり、ここでは適切と思われます。しかし、実際の例からあまり気を取られたくありませんでした。
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);
}
}
関連付けられたタイプ を使用することにより、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
}
}
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;
}
しかし、私はこの最後のものに頭を回すのに苦労しています。