第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を開催した。
ヒープとかスタックとかメモリ周りが絡んでくるとつらい...でも考え方は分かってきた気がする。
今週のプルリクエストはこちら。