目次

Rustの勉強[CLI作成編 その5]

(更新: )ぎじゅつ

はじめに

#

NO IMAGEThe Rust Programming Language 日本語版 - The Rust Programming Language 日本語版
を読んでいる

手を動かす系の勉強なのでブログで書くことがない

お勉強

#

メモ

#

NO IMAGEリファクタリングしてモジュール性とエラー処理を向上させる - The Rust Programming Language 日本語版

  • これをやる

  • 色々ぐちゃぐちゃに作ってしまったので整理からやる

  • コンストラクタね

したがって、今やparse_config関数の目的はConfigインスタンスを生成することになったので、 parse_configをただの関数からConfig構造体に紐づくnewという関数に変えることができます。

  • 確かにね

  • ふたつのPCでやってたからぐちゃぐちゃになってる

  • gitうぜーって久しぶりに思った

  • ひたすら直してコンストラクタを作った

  • cargo runしたら壊れた

  • ひたすら直せたけど、LSPが警告出さなかったのが謎

 cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/minigrep`

thread 'main' (11967418) panicked at src/main.rs:32:21:
index out of bounds: the len is 1 but the index is 1
stack backtrace:
   0: __rustc::rust_begin_unwind
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/panicking.rs:698:5
   1: core::panicking::panic_fmt
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/core/src/panicking.rs:80:14
   2: core::panicking::panic_bounds_check
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/core/src/panicking.rs:276:5
   3: minigrep::Config::new
             at ./src/main.rs:32:21
   4: minigrep::main
             at ./src/main.rs:8:18
   5: core::ops::function::FnOnce::call_once
             at /nix/store/1mnwkvhnnkbsbphhaiq1pw6gi8qm25gk-rust-default-1.92.0/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
  • panicしたね
  • 引数なしの場合
use std::env;
use std::fs::File;
use std::io::prelude::*;

fn main() {
    let args: Vec<String> = env::args().collect();
    // {}を探しています
    let config = Config::new(&args);

    println!("Searching for {}", config.query);
    println!("In file {}", config.filename);

    let mut f = File::open(config.filename).expect("file not found");

    let mut contents = String::new();
    f.read_to_string(&mut contents)
        // ファイルの読込中に問題がありました
        .expect("something went wrong reading the file");

    // テキストは\n{}です
    println!("With text:\n{}", contents);
}

#[derive(Debug)]
struct Config {
    query: String,
    filename: String,
}

impl Config {
    fn new(args: &[String]) -> Config {
        if args.len() < 3 {
            panic!("not enough arguments")
        }
        let query = args[1].clone();
        let filename = args[2].clone();

        Config { query, filename }
    }
}
  • ということで直した
 cargo run
   Compiling minigrep v0.1.0 (/Users/ryu.aizawa/Repo/minigrep)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.13s
     Running `target/debug/minigrep`

thread 'main' (11981591) panicked at src/main.rs:33:13:
not enough arguments
stack backtrace:
   0: __rustc::rust_begin_unwind
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/std/src/panicking.rs:698:5
   1: core::panicking::panic_fmt
             at /rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/library/core/src/panicking.rs:80:14
   2: minigrep::Config::new
             at ./src/main.rs:33:13
   3: minigrep::main
             at ./src/main.rs:8:18
   4: core::ops::function::FnOnce::call_once
             at /nix/store/1mnwkvhnnkbsbphhaiq1pw6gi8qm25gk-rust-default-1.92.0/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

panic!を呼び出す代わりにnewからResultを返す

  • いやそりゃそうか、実装しないと発想がすぐにでてこなかった

  • ????

    • Result<Config, Err>って型だと思ったらErr → &'static strって記述で意味分からんってなってきた
n new(args: &[String]) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("not enough arguments");
        }
  • これなんで式なのにreturn書く必要あるんだろう
  • geminiするか
  1. なぜ式なのに return しているのか?
    Rustでは「関数の最後に書かれた式」が戻り値になりますが、関数の途中で処理を切り上げて値を返したい場合は return キーワードが必須になります。
  • えーそうだっけ
  • でもこれもif式じゃない?
  • 理解した、ifを式として使うならelseも書けっていわれた

なぜここでライフタイムが必要なの?
Rustのコンパイラは、参照(&)を扱うとき、「その参照先がいつまで存在しているか」を厳しくチェックします。
今回返している "not enough arguments" という文字列リテラルは、バイナリの中に直接書き込まれており、プログラムが終了するまで消えません。そのため、ライフタイムは自動的に 'static になります。

  • だから'staticってなんやねん!!!

'staticを一言でいうと

**「プログラムが開始してから終了するまで、メモリ上の同じ場所にずっと存在し続けること」**を保証する特別なライフタイムです。

  • んーーやったっけ?
  • 今、思いだしたが'selfは確実にやった(今回でてきてないけど)

今回のような「文字列リテラル」を返すとき: プログラムに直接書き込んだメッセージをそのまま使いたい場合。これは最も一般的で安全な 'static の使い方です。

  • あーそうか、今回って直接書き込んでるからそもそも参照先がないのか
  • こいつがmoveする動きをしないならほかの何のライフタイムに依存するかわからない状態ってことか
  • だから本来はメソッド内で定義された文字列などはreturnできない、なぜならスコープ内でライフタイムが終了するから

この "not enough arguments" は、関数が呼ばれた時に作られるのではなく、プログラムが起動した時からバイナリの中に存在しています。

  • なるほどなんだけど、それってconstじゃねって思っちゃった

  • いや違うか?

  • gemini的にはほぼ同じようなものっていわれた

  • あとはなんでmoveしないかわからん

  • あー理解した

  • constのようにコンパイル時に決定してしまう値はそもそもmove自体できないんだ

  • だから'staticで存在しないライフタイムを意図的に明記しているんだ

まとめ

#
  • ひたすらに実装するの楽しい

  • 勉強したResultとかスっとでてこなかったがここで実装できるというのが良かった

  • static &strが謎って書いていたのだが、たくさん質問したことによって解決した

  • stackとheapの考えも再度確認して、性質的に可変かどうかでコンパイル時に長さがわかるのが大事だった

    • stackはコンパイル時にわかっていてほしいbool, i32とか
    • heapはVecとか
  • 30mが1hの勉強になってしまった

  • 次はここ

NO IMAGEリファクタリングしてモジュール性とエラー処理を向上させる - The Rust Programming Language 日本語版