第24回です。
前回はこちら。
[第24回の様子]
2022/01/05に第24回を開催した。
内容としてはRust By Example 日本語版の「9. 関数」、「9.1. メソッド」まで取り組んだ。
参加者は5人。年始だし忘れずにきてくれただけでもありがたい...。
[学んだこと]
- 9. 関数
- rustの関数は
fn
キーワードを用いて定義する - 返り値の型は
->
の後に書く
fn is_divisible_by(lhs: u32, rhs: u32) -> bool { // 早期リターンの場合はreturnが必須 if rhs == 0 { return false; } // 最後の式が返り値になる、return不要 lhs % rhs == 0 }
- 値を返さない関数の場合、ユニット型(
()
)を返すのと同義
// 返り値の型指定なし fn main() { // do something } // 返り値の型指定あり fn main() -> () { // do something }
- C/C++とは違い、関数の定義を行う順番は制限なし
fn main() { // 後ろで定義された関数を呼び出せる fizzbuzz_to(100); } fn fizzbuzz_to(n: u32) { // print fizz buzz }
struct Point { x: f64, y: f64, } struct Rectangle { p1: Point, p2: Point, } impl Rectangle { // `&self`は`self: &Self`の糖衣構文 fn area(&self) -> f64 { // インスタンス変数を参照し利用する let Point { x: x1, y: y1 } = self.p1; let Point { x: x2, y: y2 } = self.p2; ((x1 - x2) * (y1 - y2)).abs() } } // メソッドの使い方 let rectangle = Rectangle { p1: Point{ x: 0.0, y: 0.0 }, p2: Point{ x: 1.0, y: 1.0 }, }; // メソッドはドット演算子を用いて呼び出す // 最初の引数`&self`は明示せずに受け渡されている // `rectangle.area()` === `Rectangle::area(&rectangle)` println!("Rectangle area: {}", rectangle.area()); // Rectangle area: 1.0
- 関連関数は型そのものに対して定義される
struct Point { x: f64, y: f64, } impl Point { // 関連関数はコンストラクタとして使用されることが多い。 fn origin() -> Point { Point { x: 0.0, y: 0.0 } } fn new(x: f64, y: f64) -> Point { Point { x: x, y: y } } } // 関連関数の使い方 let p1 = Point::origin(); let p2 = Point::new(3.0, 4.0);
- オブジェクトの要素を変更するメソッドを作る場合、
mut
キーワードが必要
struct Point { x: f64, y: f64, } struct Rectangle { p1: Point, p2: Point, } impl Rectangle { // `&mut self`は`self: &mut Self`の糖衣構文 fn translate(&mut self, x: f64, y: f64) { self.p1.x += x; self.p2.x += x; self.p1.y += y; self.p2.y += y; } } // 使い方 let mut square = Rectangle { p1: Point{ x: 0.0, y: 0.0 }, p2: Point{ x: 1.0, y: 1.0 }, }; square.translate(1.0, 1.0); // Rectangle { p1: Point { x: 1.0, y: 1.0 }, p2: Point { x: 2.0, y: 2.0 } }
- オブジェクトのもつ要素を消費するメソッドも作れる
// `Pair`はヒープ上の整数を2つ保持する。 struct Pair(Box<i32>, Box<i32>); impl Pair { // `self`は`self: Self`の糖衣構文 // &がないので参照ではなく実体を消費する fn destroy(self) { // `self`をデストラクト let Pair(first, second) = self; println!("Destroying Pair({}, {})", first, second); // `first`、`second`はdestroy()メソッドの呼び出し後、解放され利用不可 } } // 以下のように2回destroy()を呼ぶとコンパイルエラー let pair = Pair(Box::new(1), Box::new(2)); pair.destroy(); pair.destroy(); // 以下のコンパイルエラー error[E0382]: use of moved value: `pair` --> src/main.rs:131:5 | 125 | let pair = Pair(Box::new(1), Box::new(2)); | ---- move occurs because `pair` has type `Pair`, which does not implement the `Copy` trait 126 | 127 | pair.destroy(); | --------- `pair` moved due to this method call ... 131 | pair.destroy(); | ^^^^ value used here after move | note: this function takes ownership of the receiver `self`, which moves `pair` --> src/main.rs:80:16
self
ではなく&self
と参照を利用すればdestroy()を2回呼び出してもコンパイルエラーにはならない(destroy()の意味はなくなってしまうが
[まとめ]
モブプログラミングスタイルでRust dojoを開催した。
ヒープとかスタックとかメモリ周りが絡んでくるとつらい...でも考え方は分かってきた気がする。
今週のプルリクエストはこちら。