Skip to content

Latest commit

 

History

History
204 lines (155 loc) · 6.6 KB

File metadata and controls

204 lines (155 loc) · 6.6 KB

% メソッドシンタックス

関数は素晴らしいのですが、データに対し複数の関数をまとめて適用したいときは困ったことになります。

このコードについて考えてみましょう。

baz(bar(foo)));

私たちはこれを左から右へ'baz bar foo'と読みますが、関数が呼び出される順番は異なり、内側から外へ'foo bar baz'となります。もし代わりにこう書けたらいいとは思いませんか?

foo.bar().baz();

もう分かっているかもしれませんが、あなたは幸いにもこう書けるのです!Rustはimplキーワードでこの「メソッド呼び出し構文」を提供しています。

メソッド呼び出し

どんな風に書けるかがこちらになります。

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

fn main() {
    let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
    println!("{}", c.area());
}

これは12.566371と出力します。

私たちは円を表す構造体を作りました。加えてimplブロックを書き、その中にareaというメソッドを定義しました。

メソッドは特別に、self&self&mut selfの3種類の内1つを第1引数に取ります。foo.bar()ならメソッドの第1引数はfooであると考えて下さい。3種類の引数はそれぞれ、selfがスタック上の値である場合、&selfが参照である場合、&mut selfがミュータブル参照である場合に対応しています。area&selfで受け取っていますから、いつもの引数と同じように扱うことができます。Circleも内容が分かっていますから、radiusへアクセスするのに普通の構造体との違いはありません。

あなたは所有権を得るよりも借用を好んで使うべきですし、ミュータブルな値や参照よりもイミュータブルな参照を使うべきですから、&selfを常用すべきです。以下が3種類全ての例です。

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    fn reference(&self) {
       println!("selfは参照で受け取ります!");
    }

    fn mutable_reference(&mut self) {
       println!("selfはミュータブル参照で受け取ります!");
    }

    fn takes_ownership(self) {
       println!("selfの所有権を受け取ります!");
    }
}

メソッド呼び出しの連鎖

ここまでで、foo.bar()というメソッドの呼び出し方は分かりましたね。ですが最初の例のfoo.bar().baz()についてはどうでしょう?これはメソッドチェーンと呼ばれており、self自体を返り値とすることで実現できます。

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }

    fn grow(&self, increment: f64) -> Circle {
        Circle { x: self.x, y: self.y, radius: self.radius + increment }
    }
}

fn main() {
    let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
    println!("{}", c.area());

    let d = c.grow(2.0).area();
    println!("{}", d);
}

返り値の型を確認して下さい。

# struct Circle;
# impl Circle {
fn grow(&self) -> Circle {
# Circle } }

単にCircleを返しているだけです。この関数により、私たちは任意のサイズの円を作成できるようになりました。

関連付けられた関数

また、あなたはselfを引数に取らずともCircleに関連付けられた関数を定義することができます。 以下はRustのコードではごくありふれたパターンです。

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    fn new(x: f64, y: f64, radius: f64) -> Circle {
        Circle {
            x: x,
            y: y,
            radius: radius,
        }
    }
}

fn main() {
    let c = Circle::new(0.0, 0.0, 2.0);
}

この関連付けられた関数は新たにCircleを生成します。この関数はref.method()ではなく、Struct::function()という構文で呼び出されることに気をつけて下さい。幾つかの言語では、関連付けられた関数を静的メソッドと呼んでいます。

Builderパターン

ユーザーが円を作成できる機能を追加したいものの、ユーザーに許可できるのはCircleの初期値を設定することだけだと仮定しましょう。なお、指定が無ければxyの値を0.0に、radius1.0に設定するものとします。Rustはメソッドのオーバーロードや名前付き引数、可変個引数といった機能がない代わりにBuilderパターンを採用しており、それは以下のようになります。

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

struct CircleBuilder {
    x: f64,
    y: f64,
    radius: f64,
}

impl CircleBuilder {
    fn new() -> CircleBuilder {
        CircleBuilder { x: 0.0, y: 0.0, radius: 1.0, }
    }

    fn x(&mut self, coordinate: f64) -> &mut CircleBuilder {
        self.x = coordinate;
        self
    }

    fn y(&mut self, coordinate: f64) -> &mut CircleBuilder {
        self.y = coordinate;
        self
    }

    fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
        self.radius = radius;
        self
    }

    fn finalize(&self) -> Circle {
        Circle { x: self.x, y: self.y, radius: self.radius }
    }
}

fn main() {
    let c = CircleBuilder::new()
                .x(1.0)
                .y(2.0)
                .radius(2.0)
                .finalize();

    println!("area: {}", c.area());
    println!("x: {}", c.x);
    println!("y: {}", c.y);
}

私たちはここでもう1つの構造体であるCircleBuilderを作成し、Builderメソッドを定義しました。また、Circleareaメソッドを定義し、加えてCircleBuilderfinalize()というメソッドを作りました。このメソッドはBuilderメソッドで設定した値を基に、最終的なCircleを生成します。今、私たちは最初の仮定をユーザーに強制させるために型システムを利用しました。このCircleBuilderの手法を使うことで、用意した方法から選んで値(今回はCircle)を生成するという制約が実現できます。