2015-09-16 9 views
5

次のような単純なコードがあります。構造体Aには特定の属性が含まれています。その属性の既存のバージョンからAの新しいインスタンスを作成したいのですが、関数の呼び出しの直後に属性の新しい値の存続期間を作るにはどうすればよいですか?スタックに割り当てられたものを返します。

pub struct A<'a> { 
    some_attr: &'a str, 
} 

impl<'a> A<'a> { 
    fn combine(orig: &'a str) -> A<'a> { 
     let attr = &*(orig.to_string() + "suffix"); 
     A { some_attr: attr } 
    } 
} 

fn main() { 
    println!("{}", A::combine("blah").some_attr); 
} 

上記のコードは

error[E0597]: borrowed value does not live long enough 
--> src/main.rs:7:22 
    | 
7 |   let attr = &*(orig.to_string() + "suffix"); 
    |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long enough 
8 |   A { some_attr: attr } 
9 |  } 
    |  - temporary value only lives until here 
    | 
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 5:1... 
--> src/main.rs:5:1 
    | 
5 |/impl<'a> A<'a> { 
6 | |  fn combine(orig: &'a str) -> A<'a> { 
7 | |   let attr = &*(orig.to_string() + "suffix"); 
8 | |   A { some_attr: attr } 
9 | |  } 
10| | } 
    | |_^ 

答えて

12

を生成し、この質問は、最も確かに前に答えたが、ここでは、コードが多少異なっていると私はそれが重要だと思うので、私は重複としてそれを閉じていませんよ。あなたがあなたの関数を定義した方法

注:

fn combine(orig: &'a str) -> A<'a> 

それが内部に正確限り提供文字列として生きるタイプAの値を返すことを言います。しかし、関数の本体は、この宣言に違反:

let attr = &*(orig.to_string() + "suffix"); 
A { 
    some_attr: attr 
} 

は、ここでは、 origから入手 新しい Stringを構築し、それのスライスを取り、 Aの内側にそれを返すようにしてみてください。しかし、 orig.to_string() + "suffix"のために作成された暗黙的な変数の存続期間は、厳密に入力パラメータの存続期間よりも短くなります。したがって、プログラムは拒否されます。

もう1つ、より実際的な方法は、to_string()によって作成された文字列と連結がどこかに存在しなければならないことです。しかし、あなたはそれを借りて返すだけです。したがって、関数が終了すると、文字列は破棄され、返されたスライスは無効になります。これはまさにRustが防止する状況です。

pub struct A { 
    some_attr: String 
} 

たり、スライスまたは所有する文字列のいずれかを格納するのにstd::borrow::Cowを使用することができます:最後の場合、あなたには

pub struct A<'a> { 
    some_attr: Cow<'a, str> 
} 

をこのあなたがA内部Stringを保存することができますいずれかを克服するために

関数は次のようになります。

fn combine(orig: &str) -> A<'static> { 
    let attr = orig.to_owned() + "suffix"; 
    A { 
     some_attr: attr.into() 
    } 
} 

関数内に文字列を作成するので、それはCowという所有のバリアントとして表されるため、結果の値には'staticの有効期間パラメータを使用できます。 origに接続することも可能ですが、そうする理由はありません。 Cow

割り当てなしのスライスから直接Aの値を作成することも可能である:

fn new(orig: &str) -> A { 
    A { some_attr: orig.into() } 
} 

ここAの寿命パラメータは、入力文字列の寿命に(生涯エリジオン経由)結ばれますスライス。この場合、借用したCowのバリアントが使用され、割り当ては行われません。

はまた、これらの方法を実行するための書式コードを必要としないので、彼らは、より効率的であるため、String sまでの文字列スライスを変換するto_owned()またはinto()を使用することをお勧めしていることに注意してください。

A(生涯の'static)をオンザフライで作成するにはどうすればできますか? 「Cowの所有変種」とはどういう意味で、なぜ'staticが可能になるのかわかりません。ここで

Cowの定義です:

pub enum Cow<'a, B> where B: 'a + ToOwned + ?Sized { 
    Borrowed(&'a B), 
    Owned(B::Owned), 
} 

それは複雑に見えますが、それは実際にはシンプルです。 Cowのインスタンスは、タイプBへの参照を含むか、特性を介してBから派生することができる所有値のいずれかを含むことがあります。 Cow<str>文字列スライスまたは所有文字列のいずれかを表すことができる、したがって

pub enum Cow<'a, str> { 
    Borrowed(&'a str), 
    Owned(String) 
} 

- :strToOwnedを実装するためOwned関連するタイプは、それがこのようになり、この列挙はstrに特化されToOwned<Owned = String>、として書かString(に等しいですCowは実際にクローンオン書き込み機能のメソッドを提供していますが、余分な割り当てを避けるために、借用または所有することができる値を保持するのによく使用されます。Cow<'a, B>Deref<Target = B>を実装するため、Cow<'a, B>から&Bを取得できます。単純なreboであなたがxの内容にかかわらずxCow<str>の場合、&*x&strです。当然、Cowの両方のバリエーションからスライスを得ることができます。

Cow::Ownedには参照が含まれていないことがわかります。Stringのみです。したがって、Cowの値がOwnedバリアントを使用して作成された場合、は任意の生涯を選択できます(生涯パラメータはジェネリック型パラメータとよく似ています;特にそれを選択するのは発信者です)。それに制限はありません。したがって、可能な最大の生涯として'staticを選択するのが理にかなっています。

orig.to_owned誰でもこの機能を呼び出すの所有権を削除しますか?それは不便なように聞こえる。

to_owned()方法はToOwned形質に属する:

pub trait ToOwned { 
    type Owned: Borrow<Self>; 
    fn to_owned(&self) -> Self::Owned; 
} 

この特性はStringに等しいOwnedstrによって実現されます。 to_owned()メソッドは、呼び出された値の所有変種を返します。この特定の場合、&strのうちStringが作成され、文字列スライスの内容を新しい割り当てに効果的にコピーします。したがって、いいえ、to_owned()は所有権移転を意味するものではなく、「スマート」なクローンを意味するようなものです。

は、私の知る限りは、文字列を伝えることができるようInto<Vec<u8>>ではなくstrを実装し、ので、どのように我々は第二の例ではinto()を呼び出すことができますか?

Into形質は非常に汎用性があり、標準ライブラリの多くのタイプに実装されています。 Intoは、通常Fromの特性によって実装されます:T: From<U>の場合、U: Into<T>です。 valueStringある場合value&strCow::Owned(value)であれば、彼らはただCow::Borrowed(value)を返す -

impl<'a> From<&'a str> for Cow<'a, str> 

impl<'a> From<String> for Cow<'a, str> 

これらの実装は非常に単純です:標準ライブラリのFromの二つの重要な実装があります。

これは&'a strStringInto<Cow<'a, str>>を実装することを意味し、そのため彼らはinto()方法でCowに変換することができます。これはまさに私の例で起こることです。into()を使ってStringまたは&strCow<str>に変換しています。この明示的な変換がなければ、タイプのミスマッチに関するエラーが発生します。

+0

ありがとうございます!しかし、私がここで理解していないことがたくさんあります。あなたはそれをオンザフライで作成しているときに、生涯の '静的'の 'A 'をどのように返すことができますか? 「牛の所有する変種」が何を意味するのか、それがなぜ「静的」を可能にするのかは不明です。私たちはいつでも 'some_attr'をいつでも修正しているわけではありません。それは通常、"書き込み時コピー "のポイントではなく、書き込みを可能にするものですか? 'orig.to_owned'はこの関数を呼び出す人の所有権を削除しますか?それは不便なように聞こえる。 'String'は' Into 'を実装していますが 'str'を実装していないと言うことができるので、第2の例では' into() 'をどのように呼び出すことができますか? – wrongusername

+0

おっと、それはたくさんの質問です!私は私の答えを更新した、うまくいけば役立つだろう:) –

関連する問題