2017-04-10 10 views
3

私は、参照または所有された値を返すために実装できるメソッドを使って特性を定義しようとしています。以下のような参照または所有された値を返すために実装できるTraitメソッド

何か:&Typeは生涯の指定が欠落しているので、

struct Type; 
trait Trait { 
    type Value; 
    fn f(&self) -> Self::Value; 
} 
impl Trait for() { 
    type Value = Type; 
    fn f(&self) -> Self::Value { 
     Type 
    } 
} 
impl Trait for (Type,) { 
    type Value = &Type; // error[E0106]: missing lifetime specifier 
    fn f(&self) -> Self::Value { 
     &self.0 
    } 
} 

コードのこの作品は、しかし動作しません。 &Typeの寿命を&self(つまりfn f<'a>(&'a self) -> &'a Type)と同じにしたいと思いますが、これをRustで表現する方法はわかりません。

私はこのコードを動作させるためにいくつかの方法を見つけることができたが、私はそれらのいずれかを愛していない:

  1. 特性自体に明示的な寿命を追加:

    trait Trait<'a> { 
        type Value; 
        fn f<'b>(&'b self) -> Self::Value where 'b: 'a; 
    } 
    impl<'a> Trait<'a> for() { 
        type Value = Type; 
        fn f<'b>(&'b self) -> Self::Value 
         where 'b: 'a 
        { 
         Type 
        } 
    } 
    impl<'a> Trait<'a> for (Type,) { 
        type Value = &'a Type; 
        fn f<'b>(&'b self) -> Self::Value 
         where 'b: 'a 
        { 
         &self.0 
        } 
    } 
    

    私はこの解決策が気に入らないのは、Traitを使用しているものは明示的な生存期間が必要であり(本質的に必要ではないと考えています)、その特性は実装するのが不必要に複雑であるようです。

    trait Trait { 
        type Value; 
        fn f<'a>(&'a self) -> Cow<'a, Self::Value>; 
    } 
    impl Trait for() { 
        type Value = Type; 
        fn f<'a>(&'a self) -> Cow<'a, Self::Value> { 
         Cow::Owned(Type) 
        } 
    } 
    impl Trait for (Type,) { 
        type Value = Type; 
        fn f<'a>(&'a self) -> Cow<'a, Self::Value> { 
         Cow::Borrowed(&self.0) 
        } 
    } 
    

    私は、このソリューションの好きではないこと().f()Cow<_>であるということである:私はに().f().into_owned()をコールする必要があると思いstd::borrow::Cowのような -

  2. または参照ではないかもしれないかもしれない何かを返します私のTypeを入手してください。これは不要なようです(特性オブジェクトとしてTraitを使用すると、実行時のオーバーヘッドが無視される可能性があります)。

    はまた、それがSelf::Valueが要件の強すぎるこれ、ToOwned(したがって、事実上、Clone)を実装することを必要とするためCowが良くないことに注意してください。そのような制約なしでCowの代替案を実装するのは、とにかく簡単です。

この問題の解決方法はありますか?標準/最も一般的なものは何ですか?

+3

「形質自体に明示的な生存時間を追加する」は、私がそれを行う方法とまったく同じです。なぜなら、あなたがなぜ「本質的に必要ではないと信じるのか」を説明することができます。なぜなら、私にとっては、値が出た後に参照を使用することを避けるために、生涯を「自己」型に結びつける必要があります範囲の。 – Shepmaster

+0

@Shepmaster:ああ、あなたは 'trait Trait 'を意味する。<'a> {type Value; fn f(& 'a self) - > Self :: Value; } '?私はそれを試したと確信していた、それは動作しませんでしたが、明らかに私は非常に間違っています。とにかく、あなたのコメントを読んで、 '' b''を使わずに特性を定義すると、 '' a''が特性の本質的なものである必要があることがわかりました。 – peoro

+1

うん、 '' impl Trait <'static> ){...} 'が、それ以外は... – Shepmaster

答えて

4

これは、タイプまたは参照を返すかどうかを選択する追加の関連オブジェクトと、いくつかのメタプログラミング魔法を使用して解決できます。

まず、いくつかのヘルパータイプ:

struct Value; 
struct Reference; 

trait ReturnKind<'a, T: ?Sized + 'a> { 
    type Type: ?Sized; 
} 
impl<'a, T: ?Sized + 'a> ReturnKind<'a, T> for Value { 
    type Type = T; 
} 
impl<'a, T: ?Sized + 'a> ReturnKind<'a, T> for Reference { 
    type Type = &'a T; 
} 

ReturnKindは、 "入力" Value、およびReferenceため&TときTを返す "タイプレベル関数" です。

そしてトレイト:

trait Trait { 
    type Value; 
    type Return: for<'a> ReturnKind<'a, Self::Value>; 

    fn f<'a>(&'a self) -> <Self::Return as ReturnKind<'a, Self::Value>>::Type; 
} 

私たちは、タイプレベルの機能ReturnKindを「呼び出す」で戻り値の型を作ります。

Returnは、を書くことができるように特性を実装する必要があります。生涯自己がどんなものになるかはわかりませんが、Returnになります。の可能寿命はHRTBReturn: for<'a> ReturnKind<'a, Value>です。

使用法:

impl Trait for() { 
    type Value = f64; 
    type Return = Value; 

    fn f(&self) -> f64 { 
     42.0 
    } 
} 

impl Trait for (f64,) { 
    type Value = f64; 
    type Return = Reference; 

    fn f(&self) -> &f64 { 
     &self.0 
    } 
} 

fn main() { 
    let a: (f64,) = (().f(),); 
    let b: &f64 = a.f(); 
    println!("{:?} {:?}", a, b); 
    // (42,) 42 
} 

Valueタイプは'static寿命を持っている場合にのみ動作します上記のこと。 Value自体の寿命が限られている場合は、この寿命をTraitで知る必要があります。

struct Value; 
struct Reference; 
struct ExternalReference; 

trait ReturnKind<'a, 's, T: ?Sized + 'a + 's> { 
    type Type: ?Sized; 
} 
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for Value { 
    type Type = T; 
} 
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for Reference { 
    type Type = &'a T; 
} 
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for ExternalReference { 
    type Type = &'s T; 
} 

trait Trait<'s> { 
    type Value: 's; 
    type Return: for<'a> ReturnKind<'a, 's, Self::Value>; 

    fn f<'a>(&'a self) -> <Self::Return as ReturnKind<'a, 's, Self::Value>>::Type; 
} 

impl Trait<'static> for() { 
    type Value = f64; 
    type Return = Value; 

    fn f(&self) -> f64 { 
     42.0 
    } 
} 

impl Trait<'static> for (f64,) { 
    type Value = f64; 
    type Return = Reference; 

    fn f(&self) -> &f64 { 
     &self.0 
    } 
} 

impl<'a> Trait<'a> for (&'a f64,) { 
    type Value = f64; 
    type Return = ExternalReference; 

    fn f(&self) -> &'a f64 { 
     self.0 
    } 

} 

fn main() { 
    let a: (f64,) = (().f(),); 
    let b: &f64 = a.f(); 
    let c: &f64 = (b,).f(); 
    println!("{:?} {:?} {:?}", a, b, c); 
    // (42,) 42 42 
} 

をしかし特性に寿命パラメータを持つことは罰金である場合、OPはすでに簡単にソリューション提供:錆doesn't support associated lifetimes yetので、それは残念ながら、Trait<'foo>のように使用する必要があります

trait Trait<'a> { 
    type Value; 
    fn f<'b>(&'b self) -> Self::Value where 'b: 'a; 
} 

impl<'a> Trait<'a> for() { 
    type Value = f64; 
    fn f<'b: 'a>(&'b self) -> Self::Value { 
     42.0 
    } 
} 

impl<'a> Trait<'a> for (f64,) { 
    type Value = &'a f64; 
    fn f<'b: 'a>(&'b self) -> Self::Value { 
     &self.0 
    } 
} 
impl<'a, 's> Trait<'s> for (&'a f64,) { 
    type Value = &'a f64; 
    fn f<'b: 's>(&'b self) -> Self::Value { 
     self.0 
    } 
} 

fn main() { 
    let a: (f64,) = (().f(),); 
    let b: &f64 = a.f(); 
    let c: &f64 = (b,).f(); 
    println!("{:?} {:?} {:?}", a, b, c); 
    // (42,) 42 42 
} 
2

@kennytmを優れた(複雑な場合)ソリューションを提示しました。私ははるかに簡単な代替案を提案したい。

  • 形質レベルで:trait Trait<'a> { ... }
  • メソッドレベルで:値の有効期間の名前を提供するために二つの可能性があり

    trait Trait { fn f<'a>(&'a self) -> ... }

後者が十分ではありません言語によってサポートされていますが、より柔軟なものもかなり複雑です。しかし、前者はしばしば十分である。したがって、苦もなく私はあなたを提示:

trait Trait<'a> { 
    type Value; 
    fn f(self) -> Self::Value; 
} 

fは、その出力を消費Selfは、それらがCopyあるとして不変の参照である場合、これは大丈夫です。

struct Type; 

impl Trait<'static> for() { 
    type Value = Type; 
    fn f(self) -> Self::Value { 
     Type 
    } 
} 

impl<'a> Trait<'a> for &'a (Type,) { 
    type Value = &'a Type; 
    fn f(self) -> Self::Value { 
     &self.0 
    } 
} 

そして、問題なく起動できます:

証拠はプディングにある

fn main(){ 
    ().f(); 
    (Type,).f(); 
} 

このソリューションは、確かに柔軟ではありません。それは非常に簡単です。

+0

提案していただきありがとうございます!正直言って私は 'trait Trait <'a> {type Value; fn f(& 'a self) - > Self :: Value; } 'これは、あなたが示唆しているもの(この特性を使用するものはどちらも明示的な生存期間が必要なもの)として使用するのと同じくらい複雑なもので、私が好む' Self'を消費しません。とにかく、他のユースケースの方が良いかもしれないこのような選択肢を見てうれしいです。 – peoro

+0

@peoro: '& 'Tに' Trait'を実装する場合、自己を使うことは問題ではないことに注意してください。 '&' a''は' Copy'です。私は 'Trait <'a> {fn f(& 'a self)}'を気にしていました。代わりに 'self'の新鮮な生涯を導入するkennytmのソリューションを使用してください。 –

関連する問題