概要
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.