質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

3回答

693閲覧

ArrayのSliceをすると所有権を保持ができない

namuyan

総合スコア76

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

1グッド

1クリップ

投稿2019/02/19 05:38

Rustのチュートリアルを終えたばかりです。rustc 1.34.0-nightly

ArrayをスライスしVectorにpushする部分があります。
しかし返り値が保持されない為にエラーが発生します。

rust

1use sha2::{Sha512, Digest}; 2use bigint::U512; 3use byteorder::{LittleEndian, WriteBytesExt}; 4 5const HASH_LENGTH: usize = 64; 6 7fn generator(address: String, nonce: u32) ->Vec<&[u8]> { 8 let vector_length = address.as_bytes().len() + 4 + HASH_LENGTH * 64; 9 let mut seed = Vec::with_capacity(vector_length); 10 // seed [address 40bytes]-[nonce 4bytes] 11 seed.extend_from_slice(address.as_bytes()); 12 seed.write_u32::<LittleEndian>(nonce).unwrap(); 13 println!("vec={} seed={:?}", vector_length, seed.len()); 14 15 // appends [address 40bytes]-[nonce 4bytes]-[hash0]-...-[hash(HASH_LENGTH)] 16 // [hashN] = SHA512([address 40bytes]-[nonce 4bytes]-[hash0]-...-[hash(N-1)]) 17 let mut hasher = Sha512::new(); 18 let mut hash_ints = Vec::with_capacity(HASH_LENGTH); 19 for _ in 0..HASH_LENGTH { 20 hasher.input(&seed); 21 let hash = hasher.result_reset(); 22 let hash = hash.as_slice(); 23 seed.extend_from_slice(hash); 24 hash_ints.push(U512::from(hash)); 25 } 26 let final_int = U512::from(hasher.result().as_slice()); 27 println!("seed={} len={}", seed.len(), hash_ints.len()); 28 29 // all hash_ints XOR with final_int 30 let xor_hash: Vec<U512> = hash_ints.iter().map( |int| *int ^ final_int).collect(); 31 println!("{:?} == {:?} ^ {:?}", xor_hash[4], hash_ints[4], final_int); 32 33 // 動かない場所 34 let mut outputs = Vec::with_capacity(HASH_LENGTH * 2); 35 for int in &xor_hash { 36 let mut bytes = [0u8; 64]; 37 int.to_little_endian(&mut bytes); 38 let low = &bytes[..32]; 39 let high = &bytes[32..]; 40 outputs.push(low); 41 outputs.push(high); 42 //outputs.push(bytes); 43 } 44 return outputs; 45} 46 47fn main() { 48 let outputs = generator("Test string".to_string(), 123456); 49 for i in &outputs { 50 println!("{:x?}", i.to_ascii_lowercase()); 51 } 52}

error

1error[E0106]: missing lifetime specifier 2 --> src\main.rs:7:49 3 | 47 | fn generator(address: String, nonce: u32) ->Vec<&[u8]> { 5 | ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static` 6 | 7 = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments

今度はライフタイム修飾子'staticを使用しました。しかし下記のエラーが目立ちます。
returns a value referencing data owned by the current function

ArrayのSliceの仕方が悪い為だと思いますが具体的な事がわかりませんでした。
どのようにすればよいのか解る方は回答をお願いします。

tachikoma👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

しかし返り値が保持されない為にエラーが発生します。

その通りです。スライス&[T]はポインタの一種で、参照先の値を所有しません。コード中のスライス&[u8]generator関数内で作った固定長配列[u8; 64]を参照しています。しかしその固定長配列は generator関数を抜ける時に forループを回るごとに ライフタイムが尽きて破棄されます。関数から返されたスライスがダングリングポインタ(不正なアドレスまたは解放済みのアドレスを指すポインタ)になってしまうためにコンパイルエラーになるのです。つまりこのケースではスライスは返せません。

outputsにはスライスのような借用する値ではなく、所有する値であるVec<u8>[u8; 32]を格納してください。たとえばVec<u8>なら以下のようになります。

rust

1fn generator(address: String, nonce: u32) ->Vec<Vec<u8>> { 2 // ... 3 let bytes = [0u8; 64]; 4 int.to_little_endian(&mut bytes); 5 let low = (&bytes[..32]).to_vec(); 6 let high = (&bytes[32..]).to_vec();

投稿2019/02/19 06:09

編集2019/02/19 06:29
tatsuya6502

総合スコア2035

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tatsuya6502

2019/02/19 06:14

最初「関数を抜ける時にライフタイムが尽きて破棄されます」と書きましたが、Ekiさんがおっしゃっているとおり「forループを回るごとに...」が正しいですね。回答を修正しました。
namuyan

2019/02/19 12:18

ループの途中でライフタイムが尽きるという感覚が次第にわかってきました。解答して頂き有難うございます。
guest

0

ベストアンサー

結論

次のように Vec<Vec<u8>> を返すようにするとよいです。

rust

1fn generator(address: String, nonce: u32) -> Vec<Vec<u8>> { 2 // ... 中略 ... 3 let mut outputs = Vec::with_capacity(HASH_LENGTH * 2); 4 for int in &xor_hash { 5 let mut bytes = [0u8; 64]; 6 int.to_little_endian(&mut bytes); 7 let low = bytes[..32].to_owned(); 8 let high = bytes[32..].to_owned(); 9 outputs.push(low); 10 outputs.push(high); 11 //outputs.push(bytes); 12 } 13}

理由

rust

1// 動かない場所 2let mut outputs = Vec::with_capacity(HASH_LENGTH * 2); 3for int in &xor_hash { 4 let mut bytes = [0u8; 64]; 5 int.to_little_endian(&mut bytes); 6 let low = &bytes[..32]; 7 let high = &bytes[32..]; 8 outputs.push(low); 9 outputs.push(high); 10 //outputs.push(bytes); 11}

まず bytes というのは、所有権が関数内に紐づいている変数です (スタック領域に確保されています) 。しかも for 文の中にいますので、 for ループの 一回分が終わるごとに消えてしまう 変数です。 lowhigh はこれに対する参照になっているので、そのようなものは関数を抜けるどころか for 文の一回のループが終わった時点でダングリングポインタということになります。 Rust はそういうことはさせず、エラーにします。

この例のように関数から結果を返したいときは、関数に依らず自分で所有権を持つデータ型を使う必要があります (つまりヒープ領域に確保するようなデータ型です) 。このようなデータ型の代表格としては、単一のものを扱う Box<T> と配列を扱う Vec<T> があります。この例では配列なので Vec<T> が最適です。

スライス [T] から Vec<T> へ変換する方法は、最初のコードのように slice.to_owned() を使うこともできますし、 Vec::from(slice) を使うことも、型が推論できる場合は slice.into() を使うこともできます。

投稿2019/02/19 06:08

Eki

総合スコア429

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

やりたいことはエラーを解消するということで合っていますか?

&T はデータの参照であってデータそのものではないです。今回参照している bytes は関数内で作られ、関数を抜けると破棄されるのでその参照は返せません。参照ではなくデータそのものを返せば問題なくなります。例えば、 &[u8] ではなく Vec<u8> を使うとエラーは出なくなります。

use bigint::U512; use byteorder::{LittleEndian, WriteBytesExt}; use sha2::{Digest, Sha512}; const HASH_LENGTH: usize = 64; // 返り値を `Vec<Vec<u8>>` にする fn generator(address: String, nonce: u32) -> Vec<Vec<u8>> { let vector_length = address.as_bytes().len() + 4 + HASH_LENGTH * 64; let mut seed = Vec::with_capacity(vector_length); // seed [address 40bytes]-[nonce 4bytes] seed.extend_from_slice(address.as_bytes()); seed.write_u32::<LittleEndian>(nonce).unwrap(); println!("vec={} seed={:?}", vector_length, seed.len()); // appends [address 40bytes]-[nonce 4bytes]-[hash0]-...-[hash(HASH_LENGTH)] // [hashN] = SHA512([address 40bytes]-[nonce 4bytes]-[hash0]-...-[hash(N-1)]) let mut hasher = Sha512::new(); let mut hash_ints = Vec::with_capacity(HASH_LENGTH); for _ in 0..HASH_LENGTH { hasher.input(&seed); let hash = hasher.result_reset(); let hash = hash.as_slice(); seed.extend_from_slice(hash); hash_ints.push(U512::from(hash)); } let final_int = U512::from(hasher.result().as_slice()); println!("seed={} len={}", seed.len(), hash_ints.len()); // all hash_ints XOR with final_int let xor_hash: Vec<U512> = hash_ints.iter().map(|int| *int ^ final_int).collect(); println!("{:?} == {:?} ^ {:?}", xor_hash[4], hash_ints[4], final_int); // 動かない場所 let mut outputs = Vec::with_capacity(HASH_LENGTH * 2); for int in &xor_hash { let mut bytes = [0u8; 64]; int.to_little_endian(&mut bytes); // スライスではなくベクタを作る let low = bytes[..32].to_vec(); let high = bytes[32..].to_vec(); outputs.push(low); outputs.push(high); //outputs.push(bytes); } return outputs; } fn main() { let outputs = generator("Test string".to_string(), 123456); for i in &outputs { println!("{:x?}", i.to_ascii_lowercase()); } }

質問からは外れますが、質問の際は以下のことに気をつけていただけると回答がしやすくなります。

  • そのコードで何をしようとしているのか書く
    • コンパイルエラーを取るだけなら複数のやり方があり得るので意図を書かないと正しい回答が得られるとは限らない
  • cargoプロジェクトの場合、依存クレートの情報も載せる
    • 簡単にはCargo.tomlも貼る
  • 出来る限り問題が起こる最小ケースで貼る
    • どこで困っているか分かりづらいですし、回答を見ても何を変えたのが重要なのか分かりづらいと思います。
  • stableコンパイラ以外のコンパイラを使っている場合はその理由を書く
    • stableコンパイラがいわゆる普通のリリース版なので特別な事情なくnightlyを使うのは推奨されません。

投稿2019/02/19 06:10

blackenedgold

総合スコア468

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

namuyan

2019/02/19 11:51

解答して頂き有難うございます。目標としているのはコンパイラーが出すエラーの意味を理解しコードを正常に動くよう修正する事です。これからは助言を参考にさせていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問