コンテンツにスキップ

Rustのエラー E0689: can't call method on ambiguous numeric type(数値の型が決まっていない)

E0689 は、数値に対してメソッドを呼んだけれど、それがどの種類の数値なのか Rust にはまだわからない、という意味のエラーです。2 は i32 かもしれないし、u8 や u64 かもしれません。そして、それぞれの型が自分の pow メソッドを持っています。どれのつもりなのかを Rust に教えてあげればいいだけです。たいていは 2_i32 や let x: f64 のように、数文字を足せば済みます。どこに書けばいいかも rustc が教えてくれます。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。

error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}`
--> src/main.rs:2:16
|
2 | let kb = 2.pow(10); // ❌ which kind of integer is `2`?
| ^^^
|
help: you must specify a concrete type for this numeric value, like `i32`
|
2 | let kb = 2_i32.pow(10); // ❌ which kind of integer is `2`?
| ++++

「can’t call method pow on ambiguous numeric type {integer}」は「型があいまいな数値 {integer} に対して、メソッド pow を呼べない」という意味です。help は「この数値には i32 のような具体的な型を指定してください」と言っていて、2_i32 という書き方を見せてくれています。

このエラーが出るのは、次のプログラムです。

fn main() {
let kb = 2.pow(10); // ❌ which kind of integer is `2`?
println!("{}", kb);
}

コメントは「2 はどの種類の整数?」という意味です(エラーになる例なので、コードはそのままにしています)。

小数でも同じことが起こります。次の例では、数値をいったん変数に入れているので、rustc は代わりに変数の型を提案してきます。

error[E0689]: can't call method `sqrt` on ambiguous numeric type `{float}`
--> src/main.rs:3:18
|
3 | let root = x.sqrt(); // ❌ is `x` an f32 or an f64?
| ^^^^
|
help: you must specify a type for this binding, like `f32`
|
2 | let x: f32 = 2.0;
| +++++

ここでの help は「この変数には f32 のような型を指定してください」という意味です。

fn main() {
let x = 2.0;
let root = x.sqrt(); // ❌ is `x` an f32 or an f64?
println!("{}", root);
}

コメントは「x は f32? それとも f64?」という意味です。

{integer} と {float} は、rustc 流の「正確な型をまだ決めていない数値」という言い方です。{integer} は何かの整数型(i8〜i128、u8〜u128、isize、usize)で、{float} は f32 か f64 です。

^^^ はメソッドの名前を指しています。メソッドは型に属しています。i32::pow、u64::pow、f32::sqrt、f64::sqrt というふうに、別々にあるのです。結果も違います(i32 には 2 の 40 乗が入りませんが、u64 には入ります)。どれかを呼ぶには、Rust はコードのその地点で型を知っていなければなりません。ところが、ここではまだわかっていないのです。

お店の人に「2つでいくらですか?」と聞くようなものです。2つって、何が? りんご2個とテレビ2台では、値段がまるで違います。「りんごを2個」と言えば、答えはすぐに出ます。

Rust には、数値のデフォルトの型があります。ほかに何も決め手がなければ、整数は i32 に、小数は f64 になります。詳しくは整数と浮動小数点数(小数)を見てください。

ただし、このデフォルトは最後の手段です。Rust はまず関数の残りの部分を読んで、ほかの何かが型を決めていないかを確かめます。たとえば、その数値を u8 を受け取る関数に渡している、といった場合です。i32 や f64 に落ち着くのは、本当に最後の最後です。メソッドの呼び出しは、そこまで待てません。引数と結果の型をチェックする前に、どの pow を呼ぶのかを知る必要があるからです。だから Rust は、当てずっぽうで決めずに、いったん止まってあなたに聞くのです。

これは、あとに続くコードは助けにならない、ということでもあります。let y: u64 = x.pow(10); と書いても E0689 になります。Rust は pow を呼ぶ前に x の型を決めなければならないのに、u64 が付いているのは結果のほうだけだからです。助けになるのは、メソッドを呼ぶ前に数値に型を付けることです。

直し方1:数値に型の接尾辞を付ける

Section titled “直し方1:数値に型の接尾辞を付ける”

2.pow(10) や 2.0.sqrt() のように、数値リテラルに直接メソッドを呼んでいるときは、これを選びます。数値のすぐあとに型を書きましょう。_ はなくてもかまいません。読みやすくするためだけのもので、2_i32 と 2i32 は同じです。

小数の場合、rustc は f32 を提案しますが、Rust のデフォルトは f64 で、こちらのほうが精度も高いです。メモリを節約したい理由がなければ、f64 を選びましょう。

fn main() {
let kb = 2_i32.pow(10); // ✅ i32
let big = 2_u64.pow(40); // ✅ u64:2^40 は i32 に収まらない
let root = 2.0_f64.sqrt(); // ✅ f64
let dist = (-7_i32).abs(); // ✅ `-` がメソッド呼び出しより先に来るように、かっこで囲む
println!("2^10 = {}", kb);
println!("2^40 = {}", big);
println!("sqrt(2) = {}", root);
println!("|-7| = {}", dist);
}

実行結果:

2^10 = 1024
2^40 = 1099511627776
sqrt(2) = 1.4142135623730951
|-7| = 7
▶ 実際に試してみよう

結果が収まるだけの大きさの型を選びましょう。2_i32.pow(40) はコンパイルできますが、オーバーフローして、デバッグビルドではパニックします。詳しくは整数のオーバーフローを見てください。

let x = 2.0; のように、数値をいったん変数に入れているときは、これを選びます。rustc の help: の行が提案しているのも、この方法です。変数名のあとに : 型 を付けましょう。

fn main() {
let x: f64 = 2.0; // ✅ これで x は f64
println!("{:.3}", x.sqrt());
let side: f64 = 3.5;
println!("面積 = {}", side.powi(2));
let temperature: i32 = -5;
println!("0度から{}度離れている", temperature.abs());
let base: u64 = 2;
let y = base.pow(10); // y も u64
println!("{}", y);
}

実行結果:

1.414
面積 = 12.25
0度から5度離れている
1024
▶ 実際に試してみよう

値のほうに接尾辞を付けて let x = 2.0_f64; と書いても同じように動きます。読みやすいと思うほうを使いましょう。詳しくは型注釈を見てください。

直し方3:ループ変数やクロージャの引数に型を付ける

Section titled “直し方3:ループ変数やクロージャの引数に型を付ける”

for i in 1..=3 や (1..=5).map(|n| ...) のように、数値が範囲から来ているときは、これを選びます。変数 i や n の型は {integer} で、型注釈を付けられる let がありません。rustc はその変数を指してくれます。

error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}`
--> src/main.rs:3:26
|
2 | for i in 1..=3 {
| - you must specify a type for this binding, like `i32`
3 | println!("{}", i.pow(2));
| ^^^

you must specify a type for this binding, like `i32` は「この変数には i32 のような型を指定してください」という意味です。

範囲の片方の端に接尾辞を付けるか、クロージャの引数に型を付けましょう。

fn main() {
for i in 1..=3_u32 { // ✅ これで範囲全体が u32 になる
println!("{}の2乗は{}", i, i.pow(2));
}
// 範囲に接尾辞を付けるか……
let total: u32 = (1..=5_u32).map(|n| n.pow(2)).sum();
// ……クロージャの引数に型を付ける
let total2: u32 = (1..=5).map(|n: u32| n.pow(2)).sum();
println!("2乗の合計: {} と {}", total, total2);
}

実行結果:

1の2乗は1
2の2乗は4
3の2乗は9
2乗の合計: 55 と 55
▶ 実際に試してみよう

let total: u32 = ... だけでは足りないことに注目してください。rustc は .sum() にたどり着く前に、クロージャの中の n の型を知る必要があるのです。範囲に接尾辞を1つ付ければ、すべて解決します。

負の数に abs()。 let x = -5; x.abs() は can't call method `abs` on ambiguous numeric type `{integer}` になります。直し方:let x: i32 = -5;。リテラルに接尾辞を付けるときは気をつけましょう。-5_i32.abs() は -(5_i32.abs()) という意味なので、結果は -5 です。(-5_i32).abs() のようにかっこで囲みましょう。

main やテストでのちょっとした計算。 let area = 3.14 * r * r; は問題なく動きます。* はメソッドではなく演算子だからです。エラーが出るのは、.sqrt()、.round()、.powi() のようなメソッドを呼んだときだけです。直し方:let r: f64 = 2.0;。

数値の Vec。 let nums = vec![1, 2, 3]; のとき、nums.iter().map(|n| n.pow(2)) は別のコード、E0599 の no method named `pow` found for reference `&{integer}`(参照 &{integer} に pow という名前のメソッドはない)になります。ここでは n が参照だからです。直し方は同じです:vec![1_i32, 2, 3] か let nums: Vec<i32> = vec![1, 2, 3]; と書きます。

理解度チェック

1. コンパイルできるのはどの行ですか?

2. ほかに何も決め手がないとき、Rust は 7 と 7.0 にどの型を付けますか?

3. for i in 0..10 { println!("{}", i.pow(2)); } が E0689 になります。いちばん簡単な直し方はどれですか?

スコア: 0 / 3