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

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

ただいまの
回答率

87.34%

RustでString型をbytesに変換するには?

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 4,061

score 41

以下の様にrの文字列型を受け取った後:

let mut r = String::new();
// some functions and returns new_data : &[u8]
r = String::from_utf8(Vec::from(new_data)).unrwap(); 
println!("result is {}", r); // "0x148de18c21abe5c1ea727e3204ccbb4cad5f4a9da758cd957f5a6d19454d47b1cb25790cfedc643d1ab730bff97c1d4283da3faacd96b7df0bfb7bdcf3d4d029"


再度このrの値をprimitive_typesクレートを使い、64bytes型に変換して受け渡しをしたいのですが、なぜか以下の様にするとエラーになってしまいます。

crate primitive_types::H512;

//  中略

let b = H512::from_slice(&r.as_bytes);
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `132`,
 right: `64`', /Users/hogehoge/.cargo/registry/src/github.com-1ecc6299db9ec823/primitive-types-0.3.0/src/lib.rs:68:1
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

どなたか方法をご教授頂けますとさいわいです。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 3

checkベストアンサー

+5

r"0x148de18c.."という16進数の文字列ですが、これは0x14といった文字をUTF-8形式で表現したものです。str::as_bytes()はそのUTF-8表現のバイト列を返すメソッドですので、0x14なら、個々の 文字コード に対応する&[0x30, 0x78, 0x31, 0x34, ..]が返されます。

このことを説明するために、str::as_bytes()で得られた&[u8]の各要素を16進数で表示するコードを書いてみました。

fn hexdump(bytes: &[u8]) {
    println!("{} bytes:", bytes.len());
    for (i, b) in bytes.iter().enumerate() {
        // b: &u8 の値を2桁の16進数で表示する
        print!("{:02.x} ", b);

        // 値を16個表示するごとに改行する
        if (i + 1) % 16 == 0 {
            println!();
        }
    }
    println!();
}

let r = "0x148de18c21abe5c1ea727e3204ccbb4cad5f4a9da758cd957f5a6d19454d47b1cb25790cfedc643d1ab730bff97c1d4283da3faacd96b7df0bfb7bdcf3d4d029".to_string();
let bytes = r.as_bytes();
hexdump(bytes);

実行すると以下のように表示されます。

130 bytes:
30 78 31 34 38 64 65 31 38 63 32 31 61 62 65 35 
63 31 65 61 37 32 37 65 33 32 30 34 63 63 62 62 
34 63 61 64 35 66 34 61 39 64 61 37 35 38 63 64 
39 35 37 66 35 61 36 64 31 39 34 35 34 64 34 37 
62 31 63 62 32 35 37 39 30 63 66 65 64 63 36 34 
33 64 31 61 62 37 33 30 62 66 66 39 37 63 31 64 
34 32 38 33 64 61 33 66 61 61 63 64 39 36 62 37 
64 66 30 62 66 62 37 62 64 63 66 33 64 34 64 30 
32 39 

一方、H512::from_slice()が期待しているのは、文字コードの並びではなく、&[0x14, 0x8d, 0xe1, 0x8c, ..]といった数値データの並びになります。つまりstr::as_bytes()ではなく、16進数の文字列を数値データの並びへと変換する関数が必要です。

そのような関数は標準ライブラリのメソッドを使って簡単に自作できると思いますが、すでにhexというクレートがあるのでそれを使う方法を紹介します。

先ほどのコードをhexクレートで書き換えると以下のようになります。

// Cargo.tomlに以下を追加
// [dependencies]
// hex = "0.3"

use hex;

let r = "0x148de18c21abe5c1ea727e3204ccbb4cad5f4a9da758cd957f5a6d19454d47b1cb25790cfedc643d1ab730bff97c1d4283da3faacd96b7df0bfb7bdcf3d4d029".to_string();
// 先頭に"0x"があるとhexクレートでデコードできないので削除する
let r = r.replacen("0x", "", 1);
// 16進数文字列をhexクレートでデコードする
let bytes = hex::decode(&r).unwrap();  // Vec<u8>型
hexdump(&bytes);
64 bytes:
14 8d e1 8c 21 ab e5 c1 ea 72 7e 32 04 cc bb 4c 
ad 5f 4a 9d a7 58 cd 95 7f 5a 6d 19 45 4d 47 b1 
cb 25 79 0c fe dc 64 3d 1a b7 30 bf f9 7c 1d 42 
83 da 3f aa cd 96 b7 df 0b fb 7b dc f3 d4 d0 29 

期待通りの値になったようです。

このように16進数文字列をhex::decode()でデコードしてから、H512::from_slice()に渡してみてください。



追記 2019-08-07

最後に恐縮ですが、この辺りの理解を深める為のリソースでオススメ等ございましたら後学の為に共有頂けると助かります。 

質問者さまがどの程度の知識をお持ちなのかわからないので、理解を深めるために必要になりそうな項目を全て列挙します。すでにご存知のものについては無視してください。


整数と文字列の内部表現(メモリ上でのデータ形式)の違いについて確認する

整数の内部表現

  • 2進法と16進法(1バイトの情報はちょうど16進数2桁で表せる)
  • 8ビット整数、32ビット整数、64ビット整数など、加減乗除などの演算をCPUがサポートしている形式
    + Rustならu8i32usize型などのスカラ型
  • 多バイト長整数(または多倍長整数) ・・・512ビット(64バイト)整数など、演算をCPUがサポートしていない形式
    + バイト列で表現することが多い。(RustならVec<u8>など)
    + Rustではたとえばnum-bigintクレートが可変長の多バイト長整数を提供する。primitive-typesクレートはEthereumなどで使われている固定長の多バイト長整数を提供する。(可変長:整数の大きさによってバイト数が変わる、固定長:バイト数が固定されている)
  • バイトの並び順 ・・・リトルエンディアンとビッグエンディアン

文字列の内部表現(文字コードとエンコーディング)

  • Rustの文字列型(strString)はUTF-8エンコーディングを使用している
  • 整数と文字列では内部表現が異なる
    + たとえば32ビット符号付き整数(Rustではi32型)の-42と、文字列(Rustでは&strまたはString型)の"-42"ではメモリ上に異なる情報が格納される
    + Rustで文字列"-42"i32型の-42に変換するには、str型のparseメソッドを使う(例:"-42".parse::<i32>()
    + その逆の変換をするにはToStringトレイトのto_stringメソッドや、format!マクロを使う

内部表現から外部表現への変換と、その逆の変換について確認する

  • 外部表現はデータをファイルに保存したり、他のプログラムとデータをやりとりするときに必要となる
  • 外部表現にはバイナリデータ(println!"{}"などで表示できない生のバイト列)とテキストデータ(表示可能な文字で構成された文字列)がある

たとえばJSON形式

  • JSONは32ビット整数や文字列などは表現できるが、多バイト長整数のようなデータは直接表現できない
  • JSONはバイナリデータを扱えない
  • 多バイト長整数のようなデータはテキストデータに変換(エンコード)する必要がある

エンコード形式には、16進数文字列や、Base64などがよく使われる

  • 前者よりも後者の方が変換後のデータサイズが小さい
  • 一般的にハッシュデータは前者、それ以外は後者が好んで使われるが、特に決まりがあるわけではない

これらの概念についてはRustに特化した包括的な説明はなさそうなので、「コンピュータにおける情報の表現」をC言語などを使って解説しているサイトを見るのがいいのかなと思います。たとえば基本的な部分はhttps://www.seiai.ed.jp/sys/text/cs/index.htmlのようなサイトを参照して、そこにない情報(多バイト長整数、16進数文字列、Base64エンコーディングなど)については、それぞれ検索して、出てきた記事を読んでみるといったイメージです。

もしこれらの概念についてはすでに理解されていて、Rustではどう扱えばいいのかだけを知りたいのなら、(宣伝になってしまいますが)たとえば私が共著で執筆した書籍で文字列などについては少し詳しく解説していますので参考になるかもしれません。今年の5月に発売されたばかりの書籍ですので、大きな書店なら置いてあると思います。

実践Rust入門(技術評論社) ISBN978-4-297-10559-4

  • 4-2-3 固定精度の整数 ・・・内部表現の説明はないのですが、桁あふれの扱いなどを説明しています
  • 4-2-5 文字 ・・・Unicodeスカラ値について説明しています
  • 4-3-4 文字列スライス(str型) ・・・UTF-8エンコーディングなどもごく簡単にですが説明しています
  • 5-2-4 String ・・・to_string()parse()Stringstrの比較(図で説明)、など
  • 7-9 ライフタイムの詳細:簡単なベクタの実装 ・・・Stringの内部表現にも使われているVec<T>を真似た型を実装することで、Rustにおける所有権の概念に加えて、データの内部表現についても学べます

書籍の概要については共著者のκeenさんが書かれたブログ記事が参考になると思います。

実践Rust入門を書いたよ

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/08/06 10:07

    素早いご返答有り難う御座いました。
    無事にこちら問題なく64bytesの数値データへとデコードする事ができました。
    最後に恐縮ですが、この辺りの理解を深める為のリソースでオススメ等ございましたら後学の為に共有頂けると助かります。

    キャンセル

  • 2019/08/07 09:04

    > 最後に恐縮ですが、この辺りの理解を深める為のリソースでオススメ等ございましたら後学の為に共有頂けると助かります。

    返信を書き始めたところ思いのほか長くなったので、回答の方に追記しました。(返信の内容がご期待に沿えているのかはちょっと自信ありません)

    キャンセル

  • 2019/08/07 09:19

    度々のご回答有り難うございます。
    今まで強い型付けに関してはぼんやりとしか扱って居なかったのですが、これをきっかけにきちんと学習したいと強く思いました。

    キャンセル

0

使ったことのないクレートなので間違えていたら申し訳ないですが、ドキュメントを読む限りprimitive_types::H512::from_slice は64バイトの&[u8]スライスが渡されることを想定しているのだと思います。

その一方で、new_dataはそのスライスそのものでなく、何らかの64バイトのバイナリ列の16進数表示(を引用符で囲んだもの)の文字列のように見えます。もしその元となったスライスなどがあるのでしたら、それをそのままH512::from_sliceに渡してみてはいかがでしょうか。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/08/05 16:50

    ご返答有り難うございます。
    ためしてみた結果、やはり同じエラーになってしまいました。

    キャンセル

0

If the length of src and the number of bytes in Self do not match.

該当部分のpanicsを見ると、パラメータのデータ長が合わない場合に発生するようですね。

https://docs.rs/primitive-types/0.3.0/primitive_types/struct.H512.html#method.from_slice

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/08/05 19:25

    有り難う御座いました。
    `String`型で出力してみた場合、512bit, 64bytes(符号を除く)となるのでその辺でしょうか?

    キャンセル

  • 2019/08/05 19:26

    先の方の回答にもあるように、64バイトのスライスを直接`H512::from_slice`に渡しても同様のエラーになってしまうのは何故でしょうか?

    キャンセル

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

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

関連した質問

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