2016-03-25 1 views
2

私は整数への参照を取り、その整数倍のベクトルを2,5回返す関数を書いています。私はそれがスタックに割り当てられています、新しい変数を定義し、関数の最後でスコープ外となるので、ボローチェッカーはナットを行く関数から借用チェッカーの問題なしに参照として新しいデータを返す方法はありますか?

fn foo(x: &i64) -> Vec<&i64> { 
    let mut v = vec![]; 
    for i in 0..5 { 
     let q = x * 2; 
     v.push(&q); 
    } 
    v 
} 

fn main() { 
    let x = 5; 
    let q = foo(&x); 
    println!("{:?}", q); 
} 

:私のようなことが何かを見て思います。

私は何をしますか?確かに、私は新しいデータを作成する関数を書くことなく人生を送ることができません! BoxCopyタイプの回避策がありますが、私は慣用的なRustソリューションに興味があります。

私はVec<i64>を返すことができたが、同じ問題に遭遇すると思いますか?主に一般的な問題の "象徴的な"問題を思いついてみてください:)

+0

*しかし、私は同じ問題に遭遇すると思います* - これを試しましたか?なぜそれは同じ問題があると思いますか? – Shepmaster

+0

@Shepmaster私はi64を新機能に割り当てたら、スタック上では、それは怒っていると思っていました。しかし、それは動作するようです。私はこれを推測しています。私は呼び出し元に所有権を戻しているので、貸し借りチェッカーは動揺しません。すなわち、借り入れはありませんか?そのような解決策は、BoxやCellのようなヒープ割り当て構造体を使用するよりも慣用的ですか? – William

+1

借用に関係していないということは間違いありません。スタックに 'i64'を割り当て、' Vec'に所有権を移してから、 'Vec'の所有権を呼び出し元に転送します。 *ヒープ割り当て構造を含むものを使用するよりも慣用的です* - 'Vec' **は**"ヒープ割り当て構造を含む "です。この場合、 'Box'や' Cell'より 'Vec'を使う方が慣れ親しんでいると言います。この具体例では、配列 '[i64; 5] 'あなたが戻ってくる数字の数を知っているからです。 – Shepmaster

答えて

4

編集:私はあなたが書いたことを気づいただけです "ボックス、コピーなどのタイプの回避策があると思いますが、私は主に慣用的な錆の解決策 "が、私はすでにすべての答えを入力しました。 :Pそして、以下の解答はです。イディオマティックな錆、これはまさに記憶の仕組みです!コンパイラがあなたを止めなくても、何か良いことが起こるわけではないので、CやC++でスタックに割り当てられたデータへのポインタを返そうとしないでください。 ;)


あなたが参照を返すいつでも、その参照関数のパラメータとなっている必要があります。言い換えれば、データへの参照を返す場合、そのデータのすべてはの外に割り当てられていなければなりません。あなたはこれを理解しているようですが、私はそれがはっきりしていることを確認したいだけです。 :)

あなたのユースケースに応じて、この問題を解決する方法はたくさんあります。この特定の例では

、あなたは、あなただけですべての参照で悩まずにfooに所有権を与えることができ、その後何のためのxを必要としないので:

fn foo(x: i64) -> Vec<i64> { 
    std::iter::repeat(x * 2).take(5).collect() 
} 

fn main() { 
    let x = 5; 
    println!("{:?}", foo(x)); 
} 

をしかし、あなたはしたくないと言ってみましょう所有権をfooに渡す。あなたははまだ限り、あなたは根本的な価値変異させたくなかったので参照のベクトルを返すことができます:

fn foo(x: &i64) -> Vec<&i64> { 
    std::iter::repeat(x).take(5).collect() 
} 

fn main() { 
    let x = 5; 
    println!("{:?}", foo(&x)); 
} 

を...と同様に、あなたは限り、あなたはしたくなかったとして根本的な価値を変異させることができ

fn foo(x: &mut i64) -> &mut i64 { 
    *x *= 2; 
    x 
} 

fn main() { 
    let mut x = 5; 
    println!("{:?}", foo(&mut x)); 
} 

...しかし、両方ともしたいと思っています。したがって、メモリを割り当てて戻したい場合は、スタック以外の場所で行う必要があります。

// Just for illustration, see the next example for a better approach 
fn foo(x: &i64) -> Vec<Box<i64>> { 
    std::iter::repeat(Box::new(x * 2)).take(5).collect() 
} 

fn main() { 
    let x = 5; 
    println!("{:?}", foo(&x)); 
} 

...私はちょうどあなたがヒープを使用する一般的な手段としてBoxの認識しているようにしたい上記とかかわら:あなたができることの一つは、ちょうどBoxを使用して、ヒープ上でそれを詰めるです。正直、単にVecを使用すると、あなたのデータはヒープに置かれることを意味し、これは動作します:

fn foo(x: &i64) -> Vec<i64> { 
    std::iter::repeat(x * 2).take(5).collect() 
} 

fn main() { 
    let x = 5; 
    println!("{:?}", foo(&x)); 
} 

上記のように、これまでご利用の場合は、別の何かを要求するかもしれませんが、ここではおそらく最も慣用的な例です。

代わりに、Cの脚本とfooの外あらかじめ割り当てるメモリからトリックを引き出し、それを基準に渡すことができます。最後に

fn foo(x: &i64, v: &mut [i64; 5]) { 
    for i in v { 
     *i = x * 2; 
    } 
} 

fn main() { 
    let x = 5; 
    let mut v = [0; 5]; // fixed-size array on the stack 
    foo(&x, &mut v); 
    println!("{:?}", v); 
} 

、機能なければならない場合および参照データを変更する必要があります。および参照自体をコピーする必要があります。およびこれらのコピーされた参照を返す必要があります。0123このため:

use std::cell::Cell; 

fn foo(x: &Cell<i64>) -> Vec<&Cell<i64>> { 
    x.set(x.get() * 2); 
    std::iter::repeat(x).take(5).collect() 
} 

fn main() { 
    let x = Cell::new(5); 
    println!("{:?}", foo(&x)); 
} 

Cellは(すべてのプリミティブ数値型が行う)Copy形質を実装する型にCell動作することに注意してくださいかかわらず、効率的かつ非意外でもあります。あなたのタイプがCopyを実装していない場合は、RefCellでこれと同じことをやりなおすことはできますが、ランタイムオーバーヘッドが軽くなり、 "借用"が間違っていると実行時にパニックが発生する可能性があります。

+0

ありがとうございます。それは100万回明らかになり、私は狂っていないことを知ってうれしい!私は考えがBoxのようなものを避けることが多いように思ったが、オーバーヘッドがなければ誰が気にするだろうか?問題は、fn内で作られたi64を使ってVec を返すのは "もっと慣用的な"ものと思われますか?ヒープ割り当ての問題を避けるには? – William

+0

@ウィリアム氏: 'Vec 'が最も慣用的なアプローチであり、答えはイエスであり、私はこれを反映するために上記を更新しました。 'Box'を使った私の例は、ヒープを使う方法がたくさんあることが明確であることを確認することでした。あなたが "新しいデータを返す"機能を持っていて、すでに 'Vec'が返されていない*場合、' Box'はそれの代わりに使うものです。 –

+0

@Williamまた、スタック割り当てよりもヒープ割り当てにオーバーヘッドがあるため、 'Box'には*オーバーヘッドはありません。あなたが*本当に最大のパフォーマンスが必要な場合(ただし早すぎる最適化などに注意してください)、以前のスタックフレームで割り当てられたメモリへの参照を渡すCスタイルのアプローチを考慮する必要があります。私は、これを説明するために 'Vec'ではなくスタック割り当ての配列を使うようにCスタイルの例を更新します。 –

関連する問題