値型から値型への型変換を行う二つのトレイトについて。
From<T>
型 T
から型 Self
への変換をサポートするトレイト。
唯一の関数 form(T) -> Self
を持つ。
Into<T>
型 Self
から型 T
への変換をサポートするトレイト。
唯一の関数 into(self) -> T
を持つ。
以下では、i32
から f32
への変換を from
と into
のそれぞれで確認している。
fn main() {
let src: i32 = 1;
let dst: f64 = f64::from(src);
assert_eq!(dst, 1.0);
let src: i32 = 1;
let dst: f64 = src.into();
assert_eq!(dst, 1.0);
}
殆どの場合、from
でも into
でも同じ処理を書ける。ちなみに筆者は、コードを短くするため、型推論先が始めからあれば into
、なければ from
を選んでいる。
fn main() {
let src = 1;
f(f64::from(src));
f(src.into());
g(f64::from(src));
g::<f64>(src.into());
}
fn f(_: f64) {}
fn g<T>(_: T) {}
以下の実装はどちらも、TypeX
→ TypeY
変換を表す。
impl From<TypeX> for TypeY
impl Into<TypeY> for TypeX
そのため、片方があれば、もう片方もあると期待される。
From
側のみを実装すれば、Into
側は自動的に実装される。
これは以下のようなブランケット実装の定義で実現されている。
impl<T, U> Into<U> for T where U: From<T> { /* ... */ }
あらゆる型に対して、恒等変換 (何もしない変換) が存在する。
これは以下のようなブランケット実装の定義で実現されている。
impl<T> From<T> for T { /* ... */ }
From
や Into
はトレイトオブジェクトの変換には向かない。
なぜなら、これらはどちらも Sized
の実装が前提になっている。
ちなみに、トレイトオブジェクトを扱う場合、IntoIterator
が参考になる。
(その逆変換 FromIterator
は Sized
を実装している点に注意。)
変換には守られるべき制限がいくつかある。
以下は From
の API ドキュメントからの抜粋。
From
による変換の定義
前述の通り、変換の定義は From
側で行う。
fn main() {
let val = MyType::from(42);
assert_eq!(val, 42.into());
}
#[derive(Debug, Eq, PartialEq)]
struct MyType(i32);
impl From<i32> for MyType {
fn from(value: i32) -> Self {
Self(value)
}
}
Into
による引数型の拡張
引数型を Into<SomeType>
とすると、SomeType
以外の型にも対応できる。
fn main() {
show_float(42.0);
show_float(42);
}
fn show_float<T: Into<f64>>(x: T) {
println!("{}", x.into())
}