第32回です。前回はこちら。
[第32回の様子]
2022/03/23に第32回を開催した。
内容としてはRust By Example 日本語版の「10.2. 構造体の場合」、「10.3. use宣言」、「10.4. super と self」に取り組んだ。
参加者は5人。いつものメンバーが大体集まってくれた。(あれ、6人だったっけ...?)
ただ、最初に集まったメンバー3人の中では自分が一番ドライバをやっていなかったので、自分がドライバを担当した。
[学んだこと]
- 10.2. 構造体の場合
- 先週はモジュールに含まれる関数とたわむれた。今週はモジュールに含まれる構造体。
- 関数と同じく、構造体もデフォルトはプライベート、かつ構造体のフィールドもプライベート。なのでアクセスする場合は以下のようにpub修飾子をつける必要がある
mod my { pub struct OpenBox<T> { pub contents: T, } } fn main() { // モジュールの構造体にアクセス let open_box = my::OpenBox { contents: "public information" }; // モジュールの構造体のフィールドにアクセス println!("The open box contains: {}", open_box.contents); }
- デフォルトはフィールドがプライベートなので、その場合は構造体を初期化できない:前回までの
private function
とかprivate module
と同様、private field
というエラーメッセージが表示される
mod my { pub struct ClosedBox<T> { contents: T, } } fn main() { let closed_box = my::ClosedBox { contents: "classified information" }; } // 以下のコンパイルエラー error[E0451]: field `contents` of struct `ClosedBox` is private --> src/main.rs:41:38 | 41 | let closed_box = my::ClosedBox { contents: "classified information" }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ private field
- このような場合でも、モジュール内からはプライベートな要素にアクセスできるので、以下のようにコンストラクタを経由すれば問題ない
mod my { pub struct ClosedBox<T> { contents: T, } impl<T> ClosedBox<T> { pub fn new(contents: T) -> ClosedBox<T> { ClosedBox { contents: contents, } } } } fn main() { let _closed_box = my::ClosedBox::new("classified information"); }
- ただ、それでは結局構造体のフィールドにあるデータを利用できないので、データをプリントする関数を実装してみる
mod my { pub struct ClosedBox<T> { contents: T, } impl<T> ClosedBox<T> { pub fn new(contents: T) -> ClosedBox<T> { ClosedBox { contents: contents, } } // データをプリントする関数を追加 pub fn print(&self) { println!("The closed box contains: {}", &self.contents); } } } fn main() { let closed_box = my::ClosedBox::new("classified information"); closed_box.print(); } // 以下のコンパイルエラー error[E0277]: `T` doesn't implement `std::fmt::Display` --> src/main.rs:25:53 | 25 | println!("The closed box contains: {}", &self.contents); | ^^^^^^^^^^^^^^ `T` cannot be formatted with the default formatter | = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `T` | 15 | impl<T: std::fmt::Display> ClosedBox<T> { | +++++++++++++++++++
T
型がstd::fmt::Display
型を実装していないので、println
マクロでフォーマットできない、と言われてしまった- ただ、修正方法として
impl<T: std::fmt::Display> ClosedBox<T>
とトレイト境界を明示すれば良いと書かれているので、実践してみる
mod my { pub struct ClosedBox<T> { contents: T, } impl<T std::fmt::Display> ClosedBox<T> { pub fn new(contents: T) -> ClosedBox<T> { ClosedBox { contents: contents, } } // データをプリントする関数を追加 pub fn print(&self) { println!("The closed box contains: {}", &self.contents); } } } fn main() { let closed_box = my::ClosedBox::new("classified information"); closed_box.print(); // The closed box contains: classified informationが出力される }
- 別解として、内部のフィールドに対するゲッターを提供するパターンも実装してみた
mod my { pub struct ClosedBox2<T> { contents: T, } impl<T> ClosedBox2<T> { pub fn new(contents: T) -> ClosedBox2<T> { ClosedBox2 { contents: contents, } } pub fn getContents(&self) -> &T { &self.contents } } } fn main() { let closed_box2 = my::ClosedBox2::new("classified information 2"); println!("The closed box 2 contains: {}", closed_box2.getContents()); // The closed box 2 contains: classified information 2が出力される }
- この場合は、
T
型のトレイト境界としてstd::fmt::Display
型を明示しなくても良い - おそらくコンストラクタに渡されている
str
型が利用されるからだろう - 10.3. use宣言
use
宣言をすると、長い名前のモジュールを新しい名前にバインドできる
// deeplyクレートのnestedモジュールのmy_first_function関数を使うと定義する use crate::deeply::nested::{ my_first_function }; fn main() { // useで宣言しているのでクレート名やモジュール名を省略して利用できる my_first_function(); }
as
キーワードで別名にバインドできる
use deeply::nested::function as other_function; mod deeply { pub mod nested { pub fn function() { println!("called `deeply::nested::function()`"); } } } fn main() { other_function(); // called `deeply::nested::function()`が出力される }
{ }
でブロックを作りその中でuse
を利用すると、同名の関数を隠す(シャドウイング)ことができる
fn function() { println!("called `function()`"); } mod deeply { pub mod nested { pub fn function() { println!("called `deeply::nested::function()`"); } } } fn main() { println!("Entering block"); { use crate::deeply::nested::function; function(); // called `function()`ではなく、 // called `deeply::nested::function()`が出力される println!("Leaving block"); } function(); // called `function()`が出力される }
- 10.4. super と self
- 10.1. パブリックとプライベートで登場した、superやselfの解説
- selfは現在のモジュールスコープを指すので、以下の場合は同じ関数を呼び出していることになる
mod my { fn function() { println!("called `my::function()`"); } pub fn indirect_call() { // この2つは同じ関数を呼び出している self::function(); function(); } } fn main() { my::indirect_call(); }
- superを使うと親スコープを参照するので、myモジュールの外側の関数が呼び出される
fn function() { println!("called `function()`"); } mod my { pub fn indirect_call() { super::function(); // called `function()`が出力される } } fn main() { my::indirect_call(); }
crate
スコープを利用すると、一番外側のスコープを参照できる
mod cool { pub fn function() { println!("called `cool::function()`"); } } mod my { mod cool { pub fn function() { println!("called `my::cool::function()`"); } } pub fn indirect_call() { // いずれも"called `my::cool::function()`"が出力される self::cool::function(); cool::function(); { use crate::cool::function as root_function; root_function(); // "called `cool::function()`"が出力される } } } fn main() { my::indirect_call(); }
[まとめ]
モブプログラミングスタイルでRust dojoを開催した。
Rustの型にハマった回でした。難しい...
今週のプルリクエストはこちら(余計なバイナリが混ざってたorz)。