ソフトウェアシステム論 (exaを読む 実践編)
要旨
メモ
講義を忘れないためにとりあえずメモ
Result<T,E>型
Result<,>ってなに
* _
はなんでもいいときに使う?
Result<T,E>はいつ使う * エラー処理のための戻り値につかう。 * Rustは例外がない。 * Result<>かpanic!を使う。 * 回復可能なResult<> * 終了させるpanic!
?演算子(?後置演算子)
- エラー委譲の省略形の書き方 実行時、エラーを想定して予めmatchで条件分岐などを書きたくない場合に用いる。 失敗すると、単に関数の実行から抜ける。
PathBuf
- パスを扱うためのライブラリ
path.push(ファイル)でパスが
path/ファイル
の形で追加される。Vec<String>
https://doc.rust-lang.org/nightly/std/convert/trait.From.html https://doc.rust-jp.rs/rust-by-example-ja/std_misc/path.html 参考
cloneをなぜするのか
他の言語のdeepコピーに相当する。 同じオブジェクトに別の名前を束縛するのではなく、新たにコピーを作る。
今回exaでcloneをする理由は所有権かライフタイムの問題からだと思われる。 参考
exaデバッグ実行
main.rs
11行目
configure_logger();
- logを取るメソッド
13行目
let args: Vec<OsString> = args_os().skip(1).collect();
- args:OsStringを持つ配列
- args_os().skip(1)がイテレータを返す
- collect()がイテレータを配列にまとめる args_os().skip(1).collect():コマンド実行の引数をコマンドそれ自体をスキップしてそれ以降の引数を配列'args'として得る。
14行目
match Exa::from_args(args.iter(), &mut stdout()) {
main.rs
2行目にuse exa::Exa
とあり、外部ライブラリファイルexa.rs
のExaメソッドをインポートしている- match式で
from_argsメソッド
に対し引数args.iter()
,stdout()
を渡す args.iter()
は配列argsからイテレータを作るstdout()
は標準出力へのハンドラを作る
from_args()
exa.rs 86行目
pub fn from_args<I>(args: I, writer: &'w mut W) -> Result<Exa<'args, 'w, W>, Misfire> where I: Iterator<Item=&'args OsString> { Options::parse(args, &LiveVars).map(move |(options, mut args)| { debug!("Dir action from arguments: {:#?}", options.dir_action); debug!("Filter from arguments: {:#?}", options.filter); debug!("View from arguments: {:#?}", options.view.mode); // List the current directory by default, like ls. // This has to be done here, otherwise git_options won’t see it. if args.is_empty() { args = vec![ OsStr::new(".") ]; } let git = git_options(&options, &args); let ignore = ignore_cache(&options); Exa { options, writer, args, git, ignore } }) }
これは途中でmap()の中に入るので注意
parse()
イテレータを解析してOptions構造体とfree filenameの配列を作成するLiveVars
はThe “real” environment variables type.
,just calling "var_os"
環境変数?よくわからない。move
するのはRustの変数のライフタイムが遅延評価のmapのクロージャの中でwriterを使うから?- 戻り値は構造体Exa
exa.rs 93行目
if args.is_empty() { args = vec![ OsStr::new(".") ]; }
- 引数がないならカレントディレクトリ
.
を引数にする
15行目
Ok(mut exa)
- match式が問題なく実行された場合の処理が書かれている
16行目
match exa.run(){
exa.run()
- 実際にファイルパスを取得する
exa.rs 115行目 run()
for file_path in &self.args { match File::from_args(PathBuf::from(file_path), None, None) { Err(e) => { exit_status = 2; writeln!(stderr(), "{:?}: {}", file_path, e)?; } Ok(f) => { if f.points_to_directory() && !self.options.dir_action.treat_dirs_as_files() { match f.to_dir() { Ok(d) => dirs.push(d), Err(e) => writeln!(stderr(), "{:?}: {}", file_path, e)?, } } else { files.push(f); } } } }
- exa.run()が成功すればexit
失敗したらエラーを吐いて終わり
File::from_args()の戻り値
File { path, parent_dir, metadata, ext, name, is_all_all }
- from_args()で親ディレクトリやファイル名を変数pathから得る
- Rustの&strとStirngの関係がPathとPathBufの関係と対応しているらしい。
An owned, mutable path (akin to String). This type provides methods like push and set_extension that mutate the path in place. It also implements Deref to Path, meaning that all methods on Path slices are available on PathBuf values as well. More details about the overall approach can be found in the module documentation.
#![allow(unused)] fn main() { use std::path::PathBuf; let path = PathBuf::from(r"C:\windows\system32.dll"); println!("{:?}",path) } >>> "C:\\windows\\system32.dll"
- &strは文字列スライスで参照のみ
- Stringは文字列のベクタ
- Pathのドキュメントを見るとpush,popはなかった。PathBufの方にあった。
- Pathを追加したり、削ったりという変更を加えるときはPathBufに入れるようだ。
filename()
file.rs 105行目
/// A file’s name is derived from its string. This needs to handle directories /// such as `/` or `..`, which have no `file_name` component. So instead, just /// use the last component as the name. pub fn filename(path: &Path) -> String { if let Some(back) = path.components().next_back() { back.as_os_str().to_string_lossy().to_string() } else { // use the path as fallback error!("Path {:?} has no last component", path); path.display().to_string() } }
- pathを正規化して後ろから(右から)ファイル名を返すメソッド
components()
配列じゃないものをイテレータにするnext_bask()
イテレータの後ろから要素を得るas_os_str()
PashをOsStr
形式に変換しファイル名に切り分けるto_string_lossy
無効なUnicodeが入っていたら、U+FFFD REPLACEMENT CHARACTER (�)に変換する 戻り値はCow型(A clone-on-write smart pointer.スマートポインタってなに?Rustのポインタ型)to_string()
String型に変換する
pathをクローンするのは、色々他にも作業があるから。
file.rs 35行目 * なぜpathをクローンするの?
ファイル名が抽出されていても、ファイルの絶対位置を調べたり (コンパイルされたファイルの検索など)、元のパスを使ったり (シンボリックリンクに続く) する操作があるため、パスを保持しておく必要があります。
read_dir()は何をして何を返すのか
dirs.rs 34行目
pub fn read_dir(path: PathBuf) -> IOResult<Dir> { info!("Reading directory {:?}", &path); let contents = fs::read_dir(&path)? .map(|result| result.map(|entry| entry.path())) .collect::<Result<_, _>>()?; println!("{:?}", contents); Ok(Dir { contents, path }) }
- read_dir()は何を返しているの?
read_dir()の戻り値は以下のような感じで変化する
read_dir()
->Result<ReadDir>
read_dir()?
->ReadDir
イテレータ
ReadDir
はResult<DirEntry>
を要素に持つイテレータ
2重mapはなにをしているの? 1回目map イテレータ
ReadDir
の各要素Result<DirEntry>
に対して 2回目mapResul<DirEntry>
の要素DirEntryに対してpath()
entry.path()
はDirEntryをpath型に変換する処理
#![allow(unused)] use std::fs; fn main() -> std::io::Result<()> { for entry in fs::read_dir(".")? { let dir = entry?; println!("{:?}", dir); println!("{:?}", dir.path()); } Ok(()) }
DirEntry("./.bash_logout") "./.bash_logout"
Playgrougndで実行する%5D%0Ause%20std%3A%3Afs%3B%0A%0Afn%20main()%20-%3E%20std%3A%3Aio%3A%3AResult%3C()%3E%20%7B%0A%20%20%20%20for%20entry%20in%20fs%3A%3Aread_dir(%22.%22)%3F%20%7B%0A%20%20%20%20%20%20%20%20let%20dir%20%3D%20entry%3F%3B%0A%20%20%20%20%20%20%20%20println!(%22%7B%3A%3F%7D%22%2C%20dir.path())%3B%0A%20%20%20%20%7D%0A%20%20%20%20Ok(())%0A%7D&edition=2018)
- contests変数に束縛するときの処理が分かりづらい。
- DirEntry公式ドキュメントがわかりやすかった。read_dir()はReadDir型を返すらしいが、DirEntryの例文がよりわかりやすかった。2重mapがないため。
run()の戻り値
exa.rs 144行目
self.print_dirs(dirs, no_files, is_only_dir, exit_status)
17行目
Ok(exit_status) => exit(exit_status),
- exa.run()が成功したのでexitする
- exa.run()の戻り値はfilename()
参考文献
PathBuf関係
- Rustメモ
- Rust 初心者が自動型変換や型変換関係のトレイトを自信を持って扱えるようになるための型変換まとめ 8 パターン
- Rust &strとStringを理解しようと思ったらsliceやmutを理解できてないことに気づいた話
- https://doc.rust-lang.org/std/path/struct.Path.html
- https://doc.rust-lang.org/nightly/std/path/struct.PathBuf.html
- rust playground%5D%0Afn%20main()%20%7B%0Ause%20std%3A%3Apath%3A%3APathBuf%3B%0A%0Alet%20path%20%3D%20PathBuf%3A%3Afrom(r%22C%3A%5Cwindows%5Csystem32.dll%22)%3B%0A%7D&edition=2018)
- https://keens.github.io/blog/2015/11/14/rustdechiisanatsu_ruwotsukuttemiru/