Rustでクラス拡張を調べた


概要

SwiftやKotlinなどにあるクラス拡張をRustではどうやるのか気になり調べてみました。

例えば自身の2倍を返却する関数をIntに追加したい場合などはSwiftであればこうなるかと思います。

Swift

extension Int {
    func double() -> Int {
        return self * 2
    }
}

print(5.double())

Rustで試す

Rustの場合はtraitで実装するようです。先ほどの2倍の例だとこんな感じでしょうか。


trait DoubleExt {
    fn double(self) -> Self;
}

impl DoubleExt for i32 {
    fn double(self) -> Self { self * 2 }
}

fn main() {
    println!("{}", 5.double());
}

Genericにしてみる

上の例だとi32だけでしたが、Genericにする方法を調べてみました。


mod foo {
    pub trait DoubleExt {
        fn double(self) -> Self;
    }

    use std::ops::Add;
    impl<T> DoubleExt for T where T: Add<Output = T> + Copy
    {
        fn double(self) -> T { self + self }
    }
}

fn main() {
    use foo::DoubleExt;

    println!("{}", 1.1.double());
    println!("{}", 2.double());
}

パッと見で理解し辛いので、一つづつ噛み砕いてみます。

Add Trait


use std::ops::Add;

+演算子に関するtraitをとりこんでいます。
2倍の算出をimplの中でself+selfで実装しているのでこちらが必要になります。

traitの実装


impl<T> DoubleExt for T where T: Add<Output = T> + Copy

T型にDoubleExtの実装をするところですね。
whereを使ったtrait境界の指定ではT: Add<Output = T>としています。
まずはstd::opts::Addの定義を見てみましょう。

pub trait Add<Rhs = Self> {
    type Output;
    fn add(self, rhs: Rhs) -> Self::Output;
}

上記のようにOutputが関連型となっているので、T: Add<Output = T>で実装側でOutputの型を決める必要があるということでしょうか。

Copy Trait

T: Add\<Output = T\> + Copy+ Copyの部分ですね
std::opts::Addの定義をさきほど確認しましたが、addメソッドは引数の値の所有権をそのままとるようになっています。
self + selfでは左側の値をaddに渡す時点で所有権が移ってしまうので、selfを右側の値に指定する時点では使えなくなってしまうようです。
そのためTがCopy Traitを実装していることを担保してあげる必要があるようです。

さいごに

ZEROBILLBANKでは一緒に働く仲間を募集中です。
ZEROBILLBANK JAPAN Inc.