第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)。