Open39

Rust の練習帳のメモ

K SAEKIK SAEKI
cargo new hello

すると以下が作成される。

tree .
.
├── Cargo.toml
└── src
    └── main.rs

main.rs の中身は以下の通り。

fn main() {
    println!("Hello, world!");
}
cargo run

すると以下のようになる。
実行ファイルは /target/debug/hello に置かれる。

tree .
.
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
└── target
    ├── CACHEDIR.TAG
    └── debug
        ├── build
        ├── deps
        │   ├── hello-c9774b34a6e1eca3
        │   ├── hello-c9774b34a6e1eca3.2r5t8a6x6ysod9l3.rcgu.o
        │   ├── hello-c9774b34a6e1eca3.3bs9ikjyeoj6vqal.rcgu.o
        │   ├── hello-c9774b34a6e1eca3.3gxre9yf6hbggtv2.rcgu.o
        │   ├── hello-c9774b34a6e1eca3.4j5iatayyh4dpk5e.rcgu.o
        │   ├── hello-c9774b34a6e1eca3.d
        │   ├── hello-c9774b34a6e1eca3.dyjs2r1dyhl4sh9.rcgu.o
        │   └── hello-c9774b34a6e1eca3.f2gl77r8focf7oj.rcgu.o
        ├── examples
        ├── hello
        ├── hello.d
        └── incremental
            └── hello-3k89lm8602rdv
                ├── s-gslj1pn8al-hx8uw1-6yezk5dj6mc3k2aoleinaxeir
                │   ├── 2r5t8a6x6ysod9l3.o
                │   ├── 3bs9ikjyeoj6vqal.o
                │   ├── 3gxre9yf6hbggtv2.o
                │   ├── 4j5iatayyh4dpk5e.o
                │   ├── dep-graph.bin
                │   ├── dyjs2r1dyhl4sh9.o
                │   ├── f2gl77r8focf7oj.o
                │   ├── query-cache.bin
                │   └── work-products.bin
                └── s-gslj1pn8al-hx8uw1.lock
K SAEKIK SAEKI

ファイル名は main.rs だけど、実行ファイルは hello になる。
実行ファイル名は Cargo.tomlpackage.name で指定する。

- name = "hello"
+ name = "koyasaseki"

として cargo clean && cargo run すると以下のようになる。
実行ファイル名が変わる。

tree .
.
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
└── target
    ├── CACHEDIR.TAG
    └── debug
        ├── build
        ├── deps
        │   ├── koyasaeki-0d153506e3c40c03
        │   ├── koyasaeki-0d153506e3c40c03.2ubaiqv6x72hbhdm.rcgu.o
        │   ├── koyasaeki-0d153506e3c40c03.4jubn5l5cpoc9wuy.rcgu.o
        │   ├── koyasaeki-0d153506e3c40c03.4k4tzs27wyglqtys.rcgu.o
        │   ├── koyasaeki-0d153506e3c40c03.4rzewetuhiwgie41.rcgu.o
        │   ├── koyasaeki-0d153506e3c40c03.4x89ou07wvwgdpx2.rcgu.o
        │   ├── koyasaeki-0d153506e3c40c03.d
        │   └── koyasaeki-0d153506e3c40c03.ut0tsegnln9z2ry.rcgu.o
        ├── examples
        ├── incremental
        │   └── koyasaeki-2583y3hi30ttr
        │       ├── s-gslj8kpacj-18lkeen-7u8mf2r39wbxpuo9zcjypxvmx
        │       │   ├── 2ubaiqv6x72hbhdm.o
        │       │   ├── 4jubn5l5cpoc9wuy.o
        │       │   ├── 4k4tzs27wyglqtys.o
        │       │   ├── 4rzewetuhiwgie41.o
        │       │   ├── 4x89ou07wvwgdpx2.o
        │       │   ├── dep-graph.bin
        │       │   ├── query-cache.bin
        │       │   ├── ut0tsegnln9z2ry.o
        │       │   └── work-products.bin
        │       └── s-gslj8kpacj-18lkeen.lock
        ├── koyasaeki
        └── koyasaeki.d
K SAEKIK SAEKI

ところで cargo clean したときに

Removed 27 files, 917.3KiB total

と表示される。 tree コマンドでも出力されるファイルが多い。
これらはなんだろう。

K SAEKIK SAEKI

依存関係の解決が deps 、再コンパイルを効率的に行うためのものが incremental みたい。
どちらもコンパイル速度改善のためにあるみたい。
容量が大きくなってきたら clean しても問題ない。
もちろん git の管理対象外にする類のもの。

K SAEKIK SAEKI

std::process::Command でコマンドを使える。
例えば以下のようにすると、 ls コマンドを実行できる。

use std::process::Command;

fn main() {
    let mut ls = Command::new("ls");
    println!("{:#?}", ls.output().unwrap());
}
Output {
    status: ExitStatus(
        unix_wait_status(
            0,
        ),
    ),
    stdout: "Cargo.lock\nCargo.toml\nsrc\ntarget\ntests\n",
    stderr: "",
}

stdout を見ると ls が実行できてることがわかる。

K SAEKIK SAEKI

2 章のメモ

K SAEKIK SAEKI

man echoecho の使い方を確認する。

ECHO(1)                                                                General Commands Manual                                                               ECHO(1)

NAME
     echowrite arguments to the standard output

SYNOPSIS
     echo [-n] [string ...]

DESCRIPTION
     The echo utility writes any specified operands, separated by single blank (‘ ’) characters and followed by a newline (\n’) character, to the standard output.

     The following option is available:

     -n    Do not print the trailing newline character.  This may also be achieved by appending ‘\c’ to the end of the string, as is done by iBCS2 compatible
           systems.  Note that this option as well as the effect of ‘\c’ are implementation-defined in IEEE Std 1003.1-2001 (“POSIX.1”) as amended by Cor. 1-2002.
           Applications aiming for maximum portability are strongly encouraged to use printf(1) to suppress the newline character.

     Some shells may provide a builtin echo command which is similar or identical to this utility.  Most notably, the builtin echo in sh(1) does not accept the -n
     option.  Consult the builtin(1) manual page.

EXIT STATUS
     The echo utility exits 0 on success, and >0 if an error occurs.

SEE ALSO
     builtin(1), csh(1), printf(1), sh(1)

STANDARDS
     The echo utility conforms to IEEE Std 1003.1-2001 (“POSIX.1”) as amended by Cor. 1-2002.

macOS 14.2                                                                 April 12, 2003                                                                 macOS 14.2
K SAEKIK SAEKI
  • echo は引数を標準出力に書き込む。
  • オプションは -n だけ。
    • -n は最後に改行文字を印字しないためのオプション。
K SAEKIK SAEKI

いろいろ書いてあるけどだいたいわかるからちゃんと読まなくてもよし。

K SAEKIK SAEKI

cargo new echor でプロジェクトを作る。
println!("{:?}", std::env::args()); でコマンドライン引数の受け取り方を見る。

fn main() {
    println!("{:?}", std::env::args());
}
cargo run
...
Args { inner: ["target/debug/echor"] }

実行ファイルのパス target/debug/echor が入っている。
引数に HelloRust を渡してみる。

Args { inner: ["target/debug/echor", "Hello", "Rust"] }

となるはず。

cargo run
...
Args { inner: ["target/debug/echor", "Hello", "Rust"] }

OK。あってそう。
つまり、 std::env::args() の返す構造体 Argsinner に引数を持っている。
ただし、1つ目は実行ファイルのパスになる。

K SAEKIK SAEKI

素のコマンドライン引数を扱うのは(おそらく)大変なのでクレート clap を使う。
本の clap はバージョンが 2.33 で、
現在の最新バージョンが 4.4.18 なのでそれなりに読み替えは必要そう。

K SAEKIK SAEKI

Cargo.tomlclap を追加する。

cargo add clap

これで Cargo.toml が以下のようになる。

[package]
name = "echor"
version = "0.1.0"
edition = "2021"

[dependencies]
clap = "4.4.18"
K SAEKIK SAEKI

VSCode (僕は VSCode を使っている)の LS が警告を出力する。
cargo build すると clap がダウンロードされて警告が消える。

K SAEKIK SAEKI

clap を使うことで --help オプションが勝手に定義されるらしい。
以下のように書いて cargo run -- --help してみる。

use clap::Command;

fn main() {
    let _matches = Command::new("echor")
        .version("0.1.0")
        .author("Koya Saeki <koyasaeki@gmail.com>")
        .about("Rust echo")
        .get_matches();
}

本だと clap::App だが、 その辺はバージョンがかなり違うから調べつつって感じかね。
動かすと以下の出力になる。

cargo run -- --help

Rust echo

Usage: echor

Options:
  -h, --help     Print help
  -V, --version  Print version
K SAEKIK SAEKI

バージョン表記もしてくれるみたいなのでやってみる。

cargo run -- -V
...
echor 0.1.0
K SAEKIK SAEKI

今度は

cargo run -- foo

としたときに foo を適当な構造体で保持したい。
本に従いつつ、最新のバージョンに合わせるとこうなる。
-n フラグの取得もやってるのでそれも真似する。

cargo run -- -n foo

...

ArgMatches {
    valid_args: [
        "text",
        "omit_newline",
        "help",
        "version",
    ],
    valid_subcommands: [],
    args: FlatMap {
        keys: [
            "omit_newline",
            "text",
        ],
        values: [
            MatchedArg {
                source: Some(
                    CommandLine,
                ),
                indices: [
                    1,
                ],
                type_id: Some(
                    bool,
                ),
                vals: [
                    [
                        AnyValue {
                            inner: bool,
                        },
                    ],
                ],
                raw_vals: [
                    [
                        "true",
                    ],
                ],
                ignore_case: false,
            },
            MatchedArg {
                source: Some(
                    CommandLine,
                ),
                indices: [
                    2,
                ],
                type_id: Some(
                    alloc::string::String,
                ),
                vals: [
                    [
                        AnyValue {
                            inner: alloc::string::String,
                        },
                    ],
                ],
                raw_vals: [
                    [
                        "foo",
                    ],
                ],
                ignore_case: false,
            },
        ],
    },
    subcommand: None,
}
cargo run -- foo

...

ArgMatches {
    valid_args: [
        "text",
        "omit_newline",
        "help",
        "version",
    ],
    valid_subcommands: [],
    args: FlatMap {
        keys: [
            "text",
            "omit_newline",
        ],
        values: [
            MatchedArg {
                source: Some(
                    CommandLine,
                ),
                indices: [
                    1,
                ],
                type_id: Some(
                    alloc::string::String,
                ),
                vals: [
                    [
                        AnyValue {
                            inner: alloc::string::String,
                        },
                    ],
                ],
                raw_vals: [
                    [
                        "foo",
                    ],
                ],
                ignore_case: false,
            },
            MatchedArg {
                source: Some(
                    DefaultValue,
                ),
                indices: [
                    2,
                ],
                type_id: Some(
                    bool,
                ),
                vals: [
                    [
                        AnyValue {
                            inner: bool,
                        },
                    ],
                ],
                raw_vals: [
                    [
                        "false",
                    ],
                ],
                ignore_case: false,
            },
        ],
    },
    subcommand: None,
}

-n の有無で構造体に入る値が違うのがわかる。
うまくいっているよう。よかった。

バージョン違いについては ↓ の変更ログを見ながら変えた。
https://github.com/clap-rs/clap/blob/master/CHANGELOG.md

K SAEKIK SAEKI

また、 引数に required オプションをつけたので、 cargo run だけで動かすとエラーになる。

cargo run

    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/echor`
error: the following required arguments were not provided:
  <TEXT>...

Usage: echor <TEXT>...

For more information, try '--help'.
K SAEKIK SAEKI

直前のコマンドの終了コードを見る。

echo $?
2

0 じゃないのでエラーだとわかる。

cargo run -- foo && echo $?

とすると最後は 0 が出力される。

K SAEKIK SAEKI

優れたコマンドラインプログラムでは、通常の出力を標準出力に、エラーメッセージを標準エラーに出力します。

コマンドラインプログラム書かないのでこういう当たり前っぽいことを書いてくれるの助かる。

K SAEKIK SAEKI

text の値と omit_newline の値を取得したい。
clap の API がかなり変わっているのでドキュメントをみつつ進めて最終的にこうなった。

use clap::{value_parser, Arg, Command};

fn main() {
    let matches = Command::new("echor")
        .version("0.1.0")
        .author("Koya Saeki <koyasaeki@gmail.com>")
        .about("Rust echo")
        .arg(
            Arg::new("text")
                .value_name("TEXT")
                .help("Input text")
                .required(true)
                .num_args(1..)
                .value_parser(value_parser!(String)),
        )
        .arg(
            Arg::new("omit_newline")
                .short('n')
                .help("Do not output print newline")
                .num_args(0),
        )
        .get_matches();

    let text: Vec<&String> = matches.get_many::<String>("text").unwrap().collect();
    let omit_newline = matches.get_flag("omit_newline");

    println!("{:#?}", text);
    println!("{:#?}", omit_newline);
}
フラグあり
cargo run -- -n foo bar
 
...

[
    "foo",
    "bar",
]
true
フラグなし
cargo run -- foo bar

...

[
    "foo",
    "bar",
]
false
K SAEKIK SAEKI

フラグを見て改行入れるかをみる。

    let text: Vec<String> = matches
        .get_many::<String>("text")
        .unwrap()
        .map(|x| x.to_string())
        .collect();
    let omit_newline = matches.get_flag("omit_newline");

    print!("{}{}", text.join(" "), if omit_newline { "" } else { "\n" });

こうすれば良い。
textVec<String> にしたのは、 Vec<&String> だと join が使えなかったため。

K SAEKIK SAEKI

テストは本の通りに書けば通る。

  • ハマった点
    • 02_echormk-outs.sh を実行してテストファイルを作成する。
    • このときに sh mk-outs.sh として実行したため、 shell で実行してしまい、 echo の結果が違うことに...
    • ./mk-outs.sh とするか、シェバンみて bash mk-outs.sh とする。
K SAEKIK SAEKI

3 章のメモ

K SAEKIK SAEKI

cat は、複数のファイルを 1 つのファイルに連結(concatenate)するので、この名前が付けられました。

そうだったんだ。ファイルの中身を標準出力するのにしか使ってなかった。

K SAEKIK SAEKI
cat a.txt
Hello
cat b.txt
World
cat a.txt b.txt > c.txt && cat c.txt
Hello
World
K SAEKIK SAEKI

man cat でマニュアルを見る。
長い英語は読みたくないので日本語マニュアルを読む。

https://nxmnpg.lemoda.net/ja/1/cat

オプションがいくつかある。行番号を表示したり、空行を表示しなかったり。

K SAEKIK SAEKI

2 章と同じようにプロジェクトを作る。

cargo new catr

クレートも追加する。

cargo add --dev assert_cmd predicates rand
cargo add clap
K SAEKIK SAEKI

この時点で cargo test した結果を載せておく。

サマリ

failures:
    all
    all_b
    all_n
    bustle
    bustle_b
    bustle_n
    bustle_stdin
    bustle_stdin_b
    bustle_stdin_n
    empty
    empty_b
    empty_n
    fox
    fox_b
    fox_n
    skips_bad_file
    spiders
    spiders_b
    spiders_n
    usage

test result: FAILED. 0 passed; 20 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.19s

error: test failed, to rerun pass `--test cli`

当然すべて失敗している。

アウトプット
cargo test
   Compiling memchr v2.7.1
   Compiling regex-syntax v0.8.2
   Compiling cfg-if v1.0.0
   Compiling libc v0.2.152
   Compiling predicates-core v1.0.6
   Compiling ppv-lite86 v0.2.17
   Compiling difflib v0.4.0
   Compiling termtree v0.4.1
   Compiling normalize-line-endings v0.3.0
   Compiling doc-comment v0.3.3
   Compiling num-traits v0.2.17
   Compiling predicates-tree v1.0.9
   Compiling getrandom v0.2.12
   Compiling wait-timeout v0.2.0
   Compiling rand_core v0.6.4
   Compiling aho-corasick v1.1.2
   Compiling rand_chacha v0.3.1
   Compiling float-cmp v0.9.0
   Compiling rand v0.8.5
   Compiling regex-automata v0.4.5
   Compiling regex v1.10.3
   Compiling bstr v1.9.0
   Compiling predicates v3.1.0
   Compiling assert_cmd v2.0.13
   Compiling catr v0.1.0 (/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr)
    Finished test [unoptimized + debuginfo] target(s) in 2.21s
     Running unittests src/main.rs (target/debug/deps/catr-0fb49176e21b3b8a)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/cli.rs (target/debug/deps/cli-40d76ff55a01c9fa)

running 20 tests
test empty_b ... FAILED
test bustle_b ... FAILED
test empty ... FAILED
test bustle_stdin_n ... FAILED
test bustle_n ... FAILED
test all ... FAILED
test bustle_stdin ... FAILED
test bustle ... FAILED
test all_n ... FAILED
test empty_n ... FAILED
test bustle_stdin_b ... FAILED
test all_b ... FAILED
test fox_b ... FAILED
test fox ... FAILED
test spiders ... FAILED
test spiders_n ... FAILED
test usage ... FAILED
test fox_n ... FAILED
test spiders_b ... FAILED
test skips_bad_file ... FAILED

failures:

---- empty_b stdout ----
thread 'empty_b' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original:
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -0,0 +1 @@
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "-b" "tests/inputs/empty.txt"`
code=0
stdout="Hello, world!\n"
stderr=""


---- bustle_b stdout ----
thread 'bustle_b' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original:      1	The bustle in a house
│        2	The morning after death
│        3	Is solemnest of industries
│        4	Enacted upon earth,—
│
│        5	The sweeping up the heart,
│        6	And putting love away
│        7	We shall not want to use again
│        8	Until eternity.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1,9 +1 @@
│   -     1	The bustle in a house
│   -     2	The morning after death
│   -     3	Is solemnest of industries
│   -     4	Enacted upon earth,—
│   -
│   -     5	The sweeping up the heart,
│   -     6	And putting love away
│   -     7	We shall not want to use again
│   -     8	Until eternity.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "-b" "tests/inputs/the-bustle.txt"`
code=0
stdout="Hello, world!\n"
stderr=""

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- empty stdout ----
thread 'empty' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original:
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -0,0 +1 @@
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "tests/inputs/empty.txt"`
code=0
stdout="Hello, world!\n"
stderr=""


---- bustle_stdin_n stdout ----
thread 'bustle_stdin_n' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original:      1	The bustle in a house
│        2	The morning after death
│        3	Is solemnest of industries
│        4	Enacted upon earth,—
│        56	The sweeping up the heart,
│        7	And putting love away
│        8	We shall not want to use again
│        9	Until eternity.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1,9 +1 @@
│   -     1	The bustle in a house
│   -     2	The morning after death
│   -     3	Is solemnest of industries
│   -     4	Enacted upon earth,—
│   -     5	
│   -     6	The sweeping up the heart,
│   -     7	And putting love away
│   -     8	We shall not want to use again
│   -     9	Until eternity.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "-n" "-"`
stdin=`
The bustle in a house
The morning after death
Is solemnest of industries
Enacted upon earth,—

The sweeping up the heart,
And putting love away
We shall not want to use again
Until eternity.

`
code=0
stdout="Hello, world!\n"
stderr=""


---- bustle_n stdout ----
thread 'bustle_n' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original:      1	The bustle in a house
│        2	The morning after death
│        3	Is solemnest of industries
│        4	Enacted upon earth,—
│        56	The sweeping up the heart,
│        7	And putting love away
│        8	We shall not want to use again
│        9	Until eternity.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1,9 +1 @@
│   -     1	The bustle in a house
│   -     2	The morning after death
│   -     3	Is solemnest of industries
│   -     4	Enacted upon earth,—
│   -     5	
│   -     6	The sweeping up the heart,
│   -     7	And putting love away
│   -     8	We shall not want to use again
│   -     9	Until eternity.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "-n" "tests/inputs/the-bustle.txt"`
code=0
stdout="Hello, world!\n"
stderr=""


---- all stdout ----
thread 'all' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original: The quick brown fox jumps over the lazy dog.
│   Don't worry, spiders,
│   I keep house
│   casually.
│   The bustle in a house
│   The morning after death
│   Is solemnest of industries
│   Enacted upon earth,—
│
│   The sweeping up the heart,
│   And putting love away
│   We shall not want to use again
│   Until eternity.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1,13 +1 @@
│   -The quick brown fox jumps over the lazy dog.
│   -Don't worry, spiders,
│   -I keep house
│   -casually.
│   -The bustle in a house
│   -The morning after death
│   -Is solemnest of industries
│   -Enacted upon earth,—
│   -
│   -The sweeping up the heart,
│   -And putting love away
│   -We shall not want to use again
│   -Until eternity.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "tests/inputs/fox.txt" "tests/inputs/spiders.txt" "tests/inputs/the-bustle.txt"`
code=0
stdout="Hello, world!\n"
stderr=""


---- bustle_stdin stdout ----
thread 'bustle_stdin' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original: The bustle in a house
│   The morning after death
│   Is solemnest of industries
│   Enacted upon earth,—
│
│   The sweeping up the heart,
│   And putting love away
│   We shall not want to use again
│   Until eternity.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1,9 +1 @@
│   -The bustle in a house
│   -The morning after death
│   -Is solemnest of industries
│   -Enacted upon earth,—
│   -
│   -The sweeping up the heart,
│   -And putting love away
│   -We shall not want to use again
│   -Until eternity.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "-"`
stdin=
The bustle in a house
The morning after death
Is solemnest of industries
Enacted upon earth,—

The sweeping up the heart,
And putting love away
We shall not want to use again
Until eternity.

`
code=0
stdout="Hello, world!\n"
stderr=""


---- bustle stdout ----
thread 'bustle' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original: The bustle in a house
│   The morning after death
│   Is solemnest of industries
│   Enacted upon earth,—
│
│   The sweeping up the heart,
│   And putting love away
│   We shall not want to use again
│   Until eternity.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1,9 +1 @@
│   -The bustle in a house
│   -The morning after death
│   -Is solemnest of industries
│   -Enacted upon earth,—
│   -
│   -The sweeping up the heart,
│   -And putting love away
│   -We shall not want to use again
│   -Until eternity.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "tests/inputs/the-bustle.txt"`
code=0
stdout="Hello, world!\n"
stderr=""


---- all_n stdout ----
thread 'all_n' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original:      1	The quick brown fox jumps over the lazy dog.
│        1	Don't worry, spiders,
│        2	I keep house
│        3	casually.
│        1	The bustle in a house
│        2	The morning after death
│        3	Is solemnest of industries
│        4	Enacted upon earth,—
│        5	
│        6	The sweeping up the heart,
│        7	And putting love away
│        8	We shall not want to use again
│        9	Until eternity.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1,13 +1 @@
│   -     1	The quick brown fox jumps over the lazy dog.
│   -     1	Don't worry, spiders,
│   -     2	I keep house
│   -     3	casually.
│   -     1	The bustle in a house
│   -     2	The morning after death
│   -     3	Is solemnest of industries
│   -     4	Enacted upon earth,—
│   -     5	
│   -     6	The sweeping up the heart,
│   -     7	And putting love away
│   -     8	We shall not want to use again
│   -     9	Until eternity.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "tests/inputs/fox.txt" "tests/inputs/spiders.txt" "tests/inputs/the-bustle.txt" "-n"`
code=0
stdout="Hello, world!\n"
stderr=""


---- empty_n stdout ----
thread 'empty_n' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original:
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -0,0 +1 @@
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "-n" "tests/inputs/empty.txt"`
code=0
stdout="Hello, world!\n"
stderr=""


---- bustle_stdin_b stdout ----
thread 'bustle_stdin_b' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original:      1	The bustle in a house
│        2	The morning after death
│        3	Is solemnest of industries
│        4	Enacted upon earth,—
│
│        5	The sweeping up the heart,
│        6	And putting love away
│        7	We shall not want to use again
│        8	Until eternity.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1,9 +1 @@
│   -     1	The bustle in a house
│   -     2	The morning after death
│   -     3	Is solemnest of industries
│   -     4	Enacted upon earth,—
│   -
│   -     5	The sweeping up the heart,
│   -     6	And putting love away
│   -     7	We shall not want to use again
│   -     8	Until eternity.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "-b" "-"`
stdin=
The bustle in a house
The morning after death
Is solemnest of industries
Enacted upon earth,—

The sweeping up the heart,
And putting love away
We shall not want to use again
Until eternity.

`
code=0
stdout="Hello, world!\n"
stderr=""


---- all_b stdout ----
thread 'all_b' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original:      1	The quick brown fox jumps over the lazy dog.
│        1	Don't worry, spiders,
│        2	I keep house
│        3	casually.
│        1	The bustle in a house
│        2	The morning after death
│        3	Is solemnest of industries
│        4	Enacted upon earth,—
│
│        5	The sweeping up the heart,
│        6	And putting love away
│        7	We shall not want to use again
│        8	Until eternity.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1,13 +1 @@
│   -     1	The quick brown fox jumps over the lazy dog.
│   -     1	Don't worry, spiders,
│   -     2	I keep house
│   -     3	casually.
│   -     1	The bustle in a house
│   -     2	The morning after death
│   -     3	Is solemnest of industries
│   -     4	Enacted upon earth,—
│   -
│   -     5	The sweeping up the heart,
│   -     6	And putting love away
│   -     7	We shall not want to use again
│   -     8	Until eternity.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "tests/inputs/fox.txt" "tests/inputs/spiders.txt" "tests/inputs/the-bustle.txt" "-b"`
code=0
stdout="Hello, world!\n"
stderr=""


---- fox_b stdout ----
thread 'fox_b' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original:      1	The quick brown fox jumps over the lazy dog.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1 +1 @@
│   -     1	The quick brown fox jumps over the lazy dog.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "-b" "tests/inputs/fox.txt"`
code=0
stdout="Hello, world!\n"
stderr=""


---- fox stdout ----
thread 'fox' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original: The quick brown fox jumps over the lazy dog.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1 +1 @@
│   -The quick brown fox jumps over the lazy dog.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "tests/inputs/fox.txt"`
code=0
stdout="Hello, world!\n"
stderr=""


---- spiders stdout ----
thread 'spiders' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original: Don't worry, spiders,
│   I keep house
│   casually.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1,3 +1 @@
│   -Don't worry, spiders,
│   -I keep house
│   -casually.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "tests/inputs/spiders.txt"`
code=0
stdout="Hello, world!\n"
stderr=""


---- spiders_n stdout ----
thread 'spiders_n' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original:      1	Don't worry, spiders,
│        2	I keep house
│        3	casually.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1,3 +1 @@
│   -     1	Don't worry, spiders,
│   -     2	I keep house
│   -     3	casually.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "--number" "tests/inputs/spiders.txt"`
code=0
stdout="Hello, world!\n"
stderr=""


---- usage stdout ----
thread 'usage' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed var.contains(USAGE)
├── var: Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "-h"`
code=0
stdout="Hello, world!\n"
stderr=""


---- fox_n stdout ----
thread 'fox_n' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original:      1	The quick brown fox jumps over the lazy dog.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1 +1 @@
│   -     1	The quick brown fox jumps over the lazy dog.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "-n" "tests/inputs/fox.txt"`
code=0
stdout="Hello, world!\n"
stderr=""


---- spiders_b stdout ----
thread 'spiders_b' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stdout, failed diff original var
├── original:      1	Don't worry, spiders,
│        2	I keep house
│        3	casually.
├── diff:
│   --- 	orig
│   +++ 	var
│   @@ -1,3 +1 @@
│   -     1	Don't worry, spiders,
│   -     2	I keep house
│   -     3	casually.
│   +Hello, world!
└── var as str: Hello, world!

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "--number-nonblank" "tests/inputs/spiders.txt"`
code=0
stdout="Hello, world!\n"
stderr=""


---- skips_bad_file stdout ----
thread 'skips_bad_file' panicked at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5:
Unexpected stderr, failed var.is_match(sAGY6pP: .* [(]os error 2[)])
├── var:
└── var as str:

command=`"/Users/koyasaeki/ghq/github.com/koyasaeki/rust-solutions/catr/target/debug/catr" "sAGY6pP"`
code=0
stdout="Hello, world!\n"
stderr=""



failures:
    all
    all_b
    all_n
    bustle
    bustle_b
    bustle_n
    bustle_stdin
    bustle_stdin_b
    bustle_stdin_n
    empty
    empty_b
    empty_n
    fox
    fox_b
    fox_n
    skips_bad_file
    spiders
    spiders_b
    spiders_n
    usage

test result: FAILED. 0 passed; 20 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.19s

error: test failed, to rerun pass `--test cli`
K SAEKIK SAEKI

catr からはコードを src/lib.rssrc/main.rs に分ける。
main.rs は基本的に lib.rsrun を呼び出す形にする。

K SAEKIK SAEKI

lib.rs では以下のように成功 or 失敗を返す MyResult を定義して、 run の返り値とする。

type MyResult<T> = Result<T, Box<dyn Error>>;

pub fn run() -> MyResult<()> {
    println!("Hello, world!");
    Ok(())
}
K SAEKIK SAEKI

main.rslib.rsrun を呼び出す形にする。

fn main() {
    if let Err(e) = catr::run() {
        eprintln!("Application error: {}", e);
        std::process::exit(1);
    }
}

if leteprintln! を使ったことがないので調べる。