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

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

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

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

Q&A

解決済

1回答

1159閲覧

Rustのstd::ioの第一引数にBytesを渡すときに.as_ref()しないといけない理由を教えてください

KOTTON

総合スコア47

Rust

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

0グッド

0クリップ

投稿2021/09/13 03:19

#質問
rustで以下のような関数があるとします

rust

1use std::fs::File; 2use std::io 3pub async fn get_requwest(url: &String) { 4 let filename = url.split("/").last().unwrap(); 5 let response = reqwest::get(url).await.unwrap(); 6 let bytes = response.bytes().await.unwrap(); 7 let mut out = File::create(filename).unwrap(); 8 io::copy(&mut bytes.as_ref(), &mut out).unwrap(); // 問題のライン 9}

これの、io::copy()関数の第一引数は、?Sizedなので不定形(配列)という意味だと思います。
ただ、なぜ &mut bytesではだめで、.as_ref()を付けると大丈夫なのか教えてもらえませんか?
as_ref()はドキュメントだと、チープな変換に使うと書かれているのですが、意味がよく分かりません。
レファレンスを返す関数なのかなとも思うんですが、それなら&&mut bytesでいいじゃないかと思うのでやはり意味が分かりません。

各種ドキュメント

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

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

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

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

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

guest

回答1

0

ベストアンサー

まず、io::copy()の第一引数ですが、 ?Sized は「 Sized でなくてもよい」という意味なので、特に配列を要求しているわけではありません。
重要なのは

rust

1where 2 R: Read, 3 W: Write,

の部分で、これは第一引数が Read トレイトを、第二引数が Write トレイトを実装していることを要求しています。
つまり、「入力から読めて出力に書けるならコピーできる」ということですね。

次に、BytesTrait Implementationsを見ると、Readは実装されていないことが分かります。
そのため、io::copy()の第一引数としては渡せないというわけです。

一方 &[u8]Readを実装しています。
https://doc.rust-lang.org/nightly/std/primitive.slice.html#impl-Read
そのため &[u8]io::copy() の第一引数として渡せます。

Bytesは内部的には[u8]を持ったデータ構造ですので、as_ref()&[u8]を取り出すことができます。そのため、as_ref()を使うとio::copy()に渡すことができます。

ちなみに「チープな」というのは「データの変換やメモリの再割り当てなどの重い操作が必要ない」といった意味合いで、Bytesの場合は内部に持った[u8]への参照を返すだけなので軽い、というようなイメージです。

投稿2021/09/13 04:07

編集2021/09/13 04:24
dalance

総合スコア86

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

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

KOTTON

2021/09/13 06:09

本当にとても丁寧な回答ありがとうございます! 教えていただきたかった部分を的確に教えてくださっていてわかりやすかったです!! ちなみになんですが、Bytesに限らず、各as_ref()が何を返すかというのはどう確認するんでしょうか?一度VScodeに書いて、戻り値の補完を見るくらいしか思いつかないのですがもっと良い方法ございますか...?
dalance

2021/09/13 07:35

私はVSCodeを使っていないので見え方などは分からないですが、エディタ上で確認する方法としては補完を見るのが良いと思います。 他に自分がやっている方法としては * docs.rsを見る * 適当にコードを書いてコンパイルエラーを見る といったところです。 前者は`as_ref`かどうかはともかく「`&[u8]`を返すメソッドがあるかな?」と検索する時に、 後者は`as_ref`を使いたいけど何が返ってくる分からないときに使うような感じです。 特にRustのエラーメッセージは型不一致が分かりやすく表示される(ことが多い)ので、慣れてくると後者の方法でも十分に感じています。
KOTTON

2021/09/13 09:39 編集

ありがとうございます!!! いくつも質問してしまい大変申し訳ないのですが、最後に1つだけ質問させてください。 このresponse.bytes().await.unwrap()を関数から返そうと思い、関数に-> Bytes と追加すると、「Bytesが存在しません」というエラーが出ました。ですがこのBytesは、reqwestクレートが個人的に使用しているbytesというクレートで、僕のプロジェクトではCargo.tomlの[dependency]に存在していません。なのでuse bytes::Bytes でのインポートは機能しませんでした。 この場合は僕もこのためだけにbytesを[dependency]に追加するというのが通常の対処法なのでしょうか...? いくつも質問してしまい、すみません。。。!
dalance

2021/09/13 10:17

はい。その対応で大丈夫(というかそれ以外に対応しようがない)です。 一般的に、あるライブラリのインターフェースが他のライブラリの型を使う場合、re-exportということがよく行われます。 例えばreqwestの場合、urlクレートのUrlという型をreqwest::Urlとして公開しています。 これによってreqwestを使う人がわざわざ[dependency]にurlクレートを追加しなくても済むようになっています。また、一部の型だけでなく、クレート全体をre-exportすることもあります。 ただ、このre-exportは特に必須ではなく、あくまでライブラリの利便性のために行われるので、Bytesに関してはre-exportされていないようです。(将来的にされる可能性はあるかもしれません) re-exportされていない場合は、[dependency]に追加するしかありません。 この場合の注意点として、reqwestで使っているbytesと[dependency]に追加したbytesのバージョンが違っている場合にコンパイルエラーになる場合があるということです。 (上で紹介したre-exportの場合、reqwestが使っているバージョンそのものが公開されるので、このような不一致は起きません) 従って、reqwestで使われているbytesと同じバージョンを[dependency]に追加する必要があります。
KOTTON

2021/09/13 10:31

ありがとうございます。 re-exportはrust bookで見た気がしますが、よくわからないまま放置していました。こういうことだったんですね!とてもためになりました。 >> この場合の注意点として、reqwestで使っているbytesと[dependency]に追加したbytesのバージョンが違っている場合にコンパイルエラーになる場合があるということです。 こんなトラップがあるなんて。。。詳しい方にお聞きしておいて良かったです! rustコミュニティの方々はみなさんとても丁寧で優しい方ばかりなのでとても安心できます。初歩的な質問ばかりすみませんでした、ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問