kdnakt blog

hello there.

Rust dojo第73回を開催した

第73回です。前回はこちら。

kdnakt.hatenablog.com

[第73回の様子]

2023/04/05に第73回を開催した。

内容としてはRust By Example 日本語版20. 標準ライブラリのその他の「20.3. Path」、「20.4. ファイル I/O」、「20.4.1. open」に取り組んだ。
参加者は自分を入れて4人。ちょっと減ったけど淡々と進めていく。

[学んだこと]

  • 20.3. Path
  • std::path::Path構造体はファイルシステムのパスを表す
    • プラットフォームごとにwindows::Pathとposix::Pathがある
  • Pathは不変:変更する場合はPathBufを使う
    • strとStringの関係がPathとPathBufの関係と同じ
  • OsStrからPathを生成する(strでない点に注意)
    • 従って、常に&strに変換できるわけではない
    • OsStrまたはOsStringへの変換は無条件に可能
use std::path::Path;

fn main() {
    let path = Path::new(".");

    // Display可能な構造体を取得
    let _display = path.display();

    // join()はOS固有のセパレータで結合しPathBufを返す
    let mut new_path = path.join("a").join("b");

    // push()でPathBufを拡張する
    new_path.push("c");
    new_path.push("myfile.tar.gz");

    // ファイル名を更新する
    new_path.set_file_name("package.tgz");

    // PathBufを文字列のスライスに変換
    match new_path.to_str() {
        None => panic!("new path is not a valid UTF-8 sequence"),
        Some(s) => println!("new path is {}", s),
    }
}

// 実行結果は以下の通り
new path is ./a/b/c/package.tgz
  • 」文字列として出力するのに毎回matchを使うのめんどくさいな、と思ったけどdisplay()を出力してみると同じ内容が得られた
    • 別の構造体にstrとして渡す場合以外は、こっちのほうが簡単で良さそう。
match new_path.to_str() {
    None => panic!("new path is not a valid UTF-8 sequence"),
    Some(s) => println!("new path is {}", s),
};
println!("new_path is {}", new_path.display());

// 出力結果は以下の通り
new path is ./a/b/c/package.tgz
new_path is ./a/b/c/package.tgz
  • どういう場合にUTF-8のstrにならないのかと思ったけど、この辺のページを見るとWindowsUTF-16らしい。OS難しい...

doc.rust-lang.org

  • 20.4. ファイル I/O
  • Fileオブジェクトはファイルディスクリプターのラッパー
  • 以下、その取り扱い方法を見ていく
  • 20.4.1. open
  • std::fs::File::open()関数を使うと読み込み専用モードでファイルを開く
  • Fileはdropされるとファイルを閉じてくれる
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

fn main() {
    let path = Path::new("hello.txt");
    let display = path.display();

    // io::Result<File>を返す
    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display, why),
        Ok(file) => file,
    };

    let mut s = String::new();

    // io::Result&usizeを返す。
    match file.read_to_string(&mut s) {
        Err(why) => panic!("couldn't read {}: {}", display, why),
        Ok(_) => print!("{} contains:\n{}", display, s),
    }

    // `file`がスコープから抜け、"hello.txt"が閉じられる。
}

// hello.txtの中身が"Hello World!"の場合、実行結果は以下の通り
hello.txt contains:
Hello World!
  • いちいちStringを変数で確保してからファイルの中身を読み込むの面倒だなと思ったら、一発でstd::fs::read_to_string()というメソッドもあった
use std::fs;

let path = Path::new("hello.txt");

// file を介さずにファイルの内容を読み込む
let file_contents: String = fs::read_to_string(path)
    .expect("couldn't open {display}");
println!("file_content: {file_contents}"); // file_content: Hello World!

[まとめ]

モブプログラミングスタイルでRust dojoを開催した。
RustっていうかOSに近い世界はやっぱりむずかしいなあ...。

今週のプルリクエストはこちら。

github.com