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

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

ただいまの
回答率

88.64%

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

解決済

回答 3

投稿

  • 評価
  • クリップ 1
  • VIEW 2,073

namuyan

score 72

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

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

use sha2::{Sha512, Digest};
use bigint::U512;
use byteorder::{LittleEndian, WriteBytesExt};

const HASH_LENGTH: usize = 64;

fn generator(address: String, nonce: u32) ->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];
        let high  = &bytes[32..];
        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());
    }
}
error[E0106]: missing lifetime specifier
 --> src\main.rs:7:49
  |
7 | fn generator(address: String, nonce: u32) ->Vec<&[u8]> {
  |                                                 ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static`
  |
  = 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の仕方が悪い為だと思いますが具体的な事がわかりませんでした。
どのようにすればよいのか解る方は回答をお願いします。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+1

結論

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

fn generator(address: String, nonce: u32) -> Vec<Vec<u8>> {
    // ... 中略 ...
    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_owned();
        let high = bytes[32..].to_owned();
        outputs.push(low);
        outputs.push(high);
        //outputs.push(bytes);
    }
}

理由

// 動かない場所
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];
    let high  = &bytes[32..];
    outputs.push(low);
    outputs.push(high);
    //outputs.push(bytes);
}

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

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

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/19 15:14

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

    キャンセル

  • 2019/02/19 21:18

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

    キャンセル

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 20:51

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

    キャンセル

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

  • ただいまの回答率 88.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る