Open20

100 exercise Rust

ひげひげ

4-4

4-3ではimplを使って手書きで演算子をオーバーロードしてきた。しかしこの方法は変化に弱い。例えば新しいフィールドが追加されるたびにeqメソッドを書き直す必要がある。そこでもっといい方法がある。それがderiveだ。deriveを使うとマクロを生成することができる。マクロはコンパイル時にRustのコードに書き換えられるので、4-3で書いたのとほぼ同等のコードを自動で生成する。もちろんフィールドに何が含まれているかも検出するので、コードの変化にも柔軟に対応する。下のコードはderiveを使ってPartialEqを付与したコードと、コンパイル時に展開されるコードだ。4-3で頑張って書いたコードがたった一行で再現できる。

元のコード
#[derive(PartialEq)]
struct Ticket {
    title: String,
    description: String,
    status: String
}
コンパイル後のコード
#[automatically_derived]
impl ::core::cmp::PartialEq for Ticket {
    #[inline]
    fn eq(&self, other: &Ticket) -> bool {
        self.title == other.title && self.description == other.description
            && self.status == other.status
    }
}
解答

#[derive(Debug, PartialEq)]
struct Ticket {
title: String,
description: String,
status: String,
}

ひげひげ

4-5 トレイト制約

ここではジェネリクス型を学習する。ジェネリクス型とはその名前の通り型の一種だ。しかし特定の型を指しているわけではない。いわば型に対する変数のようなものだ。関数を定義するときu32やStringなど具体的な型ではなく、ジェネリクス型を使用することで、関数を一度定義するだけで様々な型ででも使えるようになる。

トレイト境界を書かなくてコンパイラーは推測できるらしい。しかし変数に型をつけることでRustに恩恵を受けるように、トレイト境界を明示することで恩恵を受けているそうだ。

ひげひげ

4-6 str型 ??????????

解答がわからない。なぜ返り値の型を&strにしただけで返り値もその型に合わせられるのか。

4-7に書いてある。

解答

    pub fn title(&self) -> &str {
       &self.title
    }
    pub fn description(&self) -> &str {
        &self.description
    }

    pub fn status(&self) -> &str {
        &self.status
    }
ひげひげ

4-7 参照解決型変換(Deref coercion)

なんもわからん

4-6で詰まっていたことがある。
self.titleはString&self.title&String型 なのに返り値は&str型だった。これはなぜか。Deref`トレイトというのが関係しているらしい。

でもあまり使いすぎない方がいいらしい。

ひげひげ

4-9 トレイト実装

トレイト実装がわかってない。<>の中に型を入れるのはなぜ?トレイト境界らしいが。。
トレイト実装をするときに、
トレイト・・・ある動作に関連するメソッドを列挙したもの。シグネチャが列挙されてる。
故に、traitで定義しているメソッドはちゃんとimpl内で定義しなければならない。これが問題のヒント

FromとIntoは全然わからん

https://rust-exercises.com/04_traits/09_from

ひげひげ

4-10 型関連とジェネリック型の使い分け

  • 型関連は型パラメーターが従属しているときに使う。また、従属している型パラメーターが1つの場合に使う。
  • ジェネリック型は???

To recap:

Use an associated type when the type must be uniquely determined for a given trait implementation.
Use a generic parameter when you want to allow multiple implementations of the trait for the same type, with different input types.

サイトでは丁寧に説明してたからもう一回読む

問題もマクロを使って解くものだから練習にいい

https://rust-exercises.com/04_traits/10_assoc_vs_generic

ひげひげ

4-12 Copyトレイト

コピートレイトを実装してはいけない主なパターンは &mutが使われてるときと、データがヒープにあるとき。前者は、借用のルールである「可変参照は2つ以上存在することができない」を破ってしまう恐れがあるから。後者は、同じアドレスを指すファットポインタが2つできてしまい、ダブルフリーを起こす可能性がある。例えば同じstrを指すStringが2つ存在し、コピー元とコピー先がdropをするとダブルフリーを起こす。こういう理屈知らなかったから勉強になった。

この理屈が大事そうだから後からよめ
That's not all, though. A few more conditions must be met:

The type doesn't manage any additional resources (e.g. heap memory, file handles, etc.) beyond the std::mem::size_of bytes that it occupies in memory.
The type is not a mutable reference (&mut T).

しかし問題が全然わからない。
https://rust-exercises.com/04_traits/12_copy

ひげひげ

4-13

p278

問題が何をやっていいかすらわからなかったのでまたやる
impldrop関数を実装しても、値をドロップする挙動は変わらず、ドロップされる際(直前か直後かは知らない)の動作をカスタマイズすることができる
https://rust-exercises.com/04_traits/13_drop

ひげひげ

4-14 Outro

大事そうなこと
Don't make a function generic if it is always invoked with a single type. It introduces indirection in your codebase, making it harder to understand and maintain.
Don't create a trait if you only have one implementation. It's a sign that the trait is not needed.
Implement standard traits for your types (Debug, PartialEq, etc.) whenever it makes sense. It will make your types more idiomatic and easier to work with, unlocking a lot of functionality provided by the standard library and ecosystem crates.
Implement traits from third-party crates if you need the functionality they unlock within their ecosystem.
Beware of making code generic solely to use mocks in your tests. The maintainability cost of this approach can be high, and it's often better to use a different testing strategy. Check out the testing masterclass for details on high-fidelity testing.

あと練習問題は無水のでやっておこう

ひげひげ

5-3

  • panic!からResult型に変換する方法がわからなかった
  • statusの型をStatusにしているので、newメソッド内では"Done", "ToDO", "InProgress"のいずれかであることが保証されている。もちろんこの3つは文字列ですらない。Done型、ToDo型, InProgress型として定義されている。Rubyのenumだと数値と値が対応しているから違和感を感じるが、Rustでは型をしていしてるだけでいい。文字列が欲しくなったら後の処理ですればいい。
    https://rust-exercises.com/05_ticket_v2/03_variants_with_data
ひげひげ

5-4 if let

  • if letif elseは分岐が1つの時に使うものであり多用するものではない。matchを使うのが多い
  • match した値の一部を取り出すのは「参照パターン」とか「ref パターン」いうもの(プログラミングRust p.222)。参照で受け取っているから参照外しをする必要がある。
  • 上級っぽい内容なのでまたいつかレベルアップしたら取り組む
    https://rust-exercises.com/05_ticket_v2/04_if_let
ひげひげ

5-5 Nullability

  • Option型を返り値とするとき、SomeNoneの2パターンに値を入れて返す。
  • &self.statusのように参照のフィールドの型は参照
  • nullのパターンを言語レベルで実装しているのでreturn if user == null みたいな定型文のアーリーリターンを書かなくていいのが良いな。nullチェックが見栄え良くなる。

https://rust-exercises.com/05_ticket_v2/05_nullability#options-definition

ひげひげ

5-7 Unwrapping

  • Result型はエラーケースを無視することができないので、matchパターンや.unwrap()を用いてエラーでないことを確認する必要がある。
  • エラー処理は呼び出し元で行う必要がある

問題を復習する
https://rust-exercises.com/05_ticket_v2/07_unwrap

ひげひげ

5-8 Error enums

  • Errorをenumにするのはわかるが、引数を入れてもいいのはへーっと思った。エラーの文言がなけりゃ原因がわからないし、全てのエラーに対してヴァリアントを作るより、カテゴリ分けして詳細はエラーメッセージとしてStringに代入するのがいい
  • エラーメッセージを表示するときの書き方が5-9でやるので鵜呑みにしない

これも復習
https://rust-exercises.com/05_ticket_v2/08_error_enums

ひげひげ

Error Trait

  • Errorトレイトを実装するのは初めてだから全然わからなかった。ErrorはOptionでのみ使うと思っていたがそうではない。
  • プログラミングRust p151に同じのがある
  • これみたいに一から実装するのではなく、thiserrorというクレートを書くと宦官にかける。でも知識として頭に入れておきたい。

むずいからまたやる
https://rust-exercises.com/05_ticket_v2/09_error_trait#display-and-debug

ひげひげ

だんだん飽きてきた。練習問題ばっか解くのは苦痛
5の残りはスキップして6に映る

ひげひげ

この問題集を作った人が書いた『Zero to Production in Rust』に移りたい。

ひげひげ

7-1 Threads

  • スライスはVecまたは配列の一部分を指定するもの。アドレスと長さをもつファットポインタでデータを持たない。