Rustの型推論は強力らしいので、どの程度強力なのか試してみました。今回は整数型を主に扱います。
基本
fn main() {
let a: i64 = 1;
let b: i32 = 1;
println!("{}", a == b) // error
}
とりあえず違う型同士を比較してみました。当然、mismatched typeエラーが出ます。
__==演算子で違う型の比較はできない__のが基本です。
基本的な型推論
fn main() {
let a = 1;
let b: i32 = 1;
println!("{}", a == b); // true
let c = 1;
let d: i64 = 1;
println!("{}", c == d) // true
}
違う型の比較はできないので、任意の型と比較した時に型推論が行われるようです。上の例では、aはi32型に、cはi64型に推論されています。それゆえ、下のコードはmismatched typeのエラーとなります。
fn main() {
let a = 1;
let b: i32 = 1;
println!("{}", a == b);
let c = 1;
let d: i64 = 1;
println!("{}", c == d);
println!("{}", a == c); // error
}
a, cがそれぞれ、i32, i64に推論された後で比較しようとしているためです。
型を指定しない状態での比較
fn main() {
println!("{}", 1 == 1); // true
let a = 1;
let b = 1;
println!("{}", a == b); // true
}
型が指定されていなくても比較できます。
推論前に比較した後別の型を推論させた場合
fn main() {
let a = 1;
let b = 1;
println!("{}", a == b);
let c: i32 = 1;
println!("{}", a == c);
// この時点で、a, b, cは全てi32
let d: i64 = 1;
println!("{}", a == d); // error
}
今回は、aとbが同じ型であることを推論させ、aがi32であることを推論させた後にbとdを比較しているためエラーとなります。
大きな整数と比較した後、型推論させようとするとどうなるか(オーバーフローについて)
fn main() {
let a = 1;
println!("{}", a == 1000); // false
let b: i8 = 1;
println!("{}", a == b); // true
}
今回は、明らかにi8ではない1000という整数とaを比較した後、i8型であると推論させています。これは、(Warningは出るものの)コンパイルを通り実行できます。理由は、1000が無理やりi8型に変換されるためです。
それゆえ、以下のコードはtrueになります。
fn main() {
let a = 1;
println!("{}", a == 257); // true
let b: i8 = 1;
println!("{}", a == b); // true
}
257は明らかにオーバーフローしているので、i8型にする際に一回りして1になります。
fn main() {
let a: i8 = 1;
let b: i8 = 257;
println!("{}", a == b); // true
println!("{}", b); // 1
}
というわけで当然こちらもtrueになります。つまりbはi8型の1ということです。
任意の型を引数にとる関数を利用した後に、型推論させるとどうなるか
use std::mem::swap;
fn main() {
let mut a = 1;
let mut b = 2;
swap(&mut a, &mut b);
println!("{}", a); // 2
println!("{}", b); // 1
let c: i8 = 1;
println!("{}", a == c); // false
println!("{}", b == c); // true
}
こういうよくわからない状況で型推論させてもちゃんと推論してくれます。そういうわけで下はエラー。
use std::mem::swap;
fn main() {
let mut a = 1;
let mut b = 2;
swap(&mut a, &mut b);
println!("{}", a);
println!("{}", b);
let c: i8 = 1;
println!("{}", a == c);
let d: i64 = 1;
println!("{}", b == d); // error
}
同じ型の値を返すだけの関数を間に挟むとどうなるか
fn main() {
let a = 1;
let b = do_nothing(a);
println!("{}", a); // 1
println!("{}", b); // 1
}
fn do_nothing<T>(x: T) -> T {
return x
}
例えばこんな状況の時、bの型を決めたらaにどんな影響が出るのか試してみます。
fn main() {
let a = 1;
let b = do_nothing(a);
println!("{}", a);
println!("{}", b);
let c: i32 = 1;
println!("{}", b == c);
let d: i64 = 1;
println!("{}", a == d); // error
}
fn do_nothing<T>(x: T) -> T {
return x
}
a == dのところでmismatched type errorとなります。
bの型がi32に定まると、aもi32になるようです。
まとめ
Rustの型推論は素直かつ優秀
以上