web-dev-qa-db-ja.com

VecまたはSliceから(std :: io :: Read)を読み取る方法は?

Vecsはstd::io::Writeをサポートしているため、たとえばFileまたはVecを使用するコードを記述できます。 APIリファレンスからは、Vecもスライスもstd::io::Readをサポートしていないようです。

これを達成するための便利な方法はありますか?ラッパー構造体を書く必要がありますか?

これは、ファイルを読み書きする作業コードの例であり、ベクトルを読み取る必要がある1行がコメント化されています。

use ::std::io;

// Generic IO
fn write_4_bytes<W>(mut file: W) -> Result<usize, io::Error>
    where W: io::Write,
{
    let len = file.write(b"1234")?;
    Ok(len)
}

fn read_4_bytes<R>(mut file: R) -> Result<[u8; 4], io::Error>
    where R: io::Read,
{
    let mut buf: [u8; 4] = [0; 4];
    file.read(&mut buf)?;
    Ok(buf)
}

// Type specific

fn write_read_vec() {
    let mut vec_as_file: Vec<u8> = Vec::new();

    {   // Write
        println!("Writing Vec... {}", write_4_bytes(&mut vec_as_file).unwrap());
    }

    {   // Read
//      println!("Reading File... {:?}", read_4_bytes(&vec_as_file).unwrap());
        //                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        //                               Comment this line above to avoid an error!
    }
}

fn write_read_file() {
    let filepath = "temp.txt";
    {   // Write
        let mut file_as_file = ::std::fs::File::create(filepath).expect("open failed");
        println!("Writing File... {}", write_4_bytes(&mut file_as_file).unwrap());
    }

    {   // Read
        let mut file_as_file = ::std::fs::File::open(filepath).expect("open failed");
        println!("Reading File... {:?}", read_4_bytes(&mut file_as_file).unwrap());
    }
}

fn main() {
    write_read_vec();
    write_read_file();
}

これはエラーで失敗します:

error[E0277]: the trait bound `std::vec::Vec<u8>: std::io::Read` is not satisfied
  --> src/main.rs:29:42
   |
29 |         println!("Reading File... {:?}", read_4_bytes(&vec_as_file).unwrap());
   |                                          ^^^^^^^^^^^^ the trait `std::io::Read` is not implemented for `std::vec::Vec<u8>`
   |
   = note: required by `read_4_bytes`

ファイルシステムに書き込むことなく、ファイル形式のエンコーダー/デコーダーのテストを作成したいと思います。

10
ideasman42

ベクトルは_std::io::Read_をサポートしていませんが、スライスはサポートしています。

Rust Vecをスライスに強制できる状況もあれば、強制できない状況もあるため、ここでは混乱が生じます。

この場合、強制が適用される段階で、コンパイラは_Vec<u8>_ しないReadを実装することを知らないため、スライスへの明示的な強制が必要です。


問題のコードは、ベクトルがread_4_bytes(&*vec_as_file)またはread_4_bytes(&vec_as_file[..])のいずれかのように強制的にスライスされたときに機能します。


注意:

  • 最初に質問したとき、私はReadの代わりに_&Read_を使用していました。これにより、スライスへの参照の受け渡しが失敗しました。ただし、_&&*vec_as_file_を渡したとは思わなかった場合を除きます。
  • 解決策を見つけてくれた_#Rust_の@areteに感謝します!
21
ideasman42

std :: io :: Cursor

std::io::Cursorは、Vec<u8>Readを実装するシンプルで便利なラッパーであるため、ベクトルを読み取り可能なエンティティとして使用できます。

let mut file = Cursor::new(vector);

read_something(&mut file);

そして documentation は、Cursorの代わりにFileを使用して単体テストを作成する方法を示しています。

実例:

use std::io::Cursor;
use std::io::Read;

fn read_something(file: &mut impl Read) {
    let _ = file.read(&mut [0; 8]);
}

fn main() {
    let vector = vec![1, 2, 3, 4];

    let mut file = Cursor::new(vector);

    read_something(&mut file);
}

ドキュメント からstd::io::Cursorについて:

カーソルは通常、メモリ内バッファで使用され、Readおよび/またはWrite ..を実装できるようにします。

標準ライブラリは、Cursor<Vec<u8>>Cursor<&[u8]>など、バッファとして一般的に使用されるさまざまなタイプにいくつかのI/O特性を実装しています。


Slice

上記の例は、スライスでも機能します。その場合、次のようになります。

read_something(&mut &vector[..]);

実例:

use std::io::Read;

fn read_something(file: &mut impl Read) {
    let _ = file.read(&mut [0; 8]);
}

fn main() {
    let vector = vec![1, 2, 3, 4];

    read_something(&mut &vector[..]);
}

&mut &vector[..]は「スライスへの可変参照」(ベクトルの一部への参照)であるため、Cursorを使用した明示的なオプションの方がより明確でエレガントであることがわかります。


カーソル<->スライス

さらに、バッファを所有するCursorがあり、たとえば「ファイル」の一部をエミュレートする必要がある場合は、sliceからCursorを取得して、関数に渡すことができます。

read_something(&mut &file.get_ref()[1..3]);
1
Alexandr Fadeev