Перейти к содержимому

Ошибка 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`?
| ++++

Компилятор говорит: нельзя вызвать метод pow у числа неопределённого типа {integer}, — и предлагает указать конкретный тип, например i32. Вот программа, которая выдаёт эту ошибку (комментарий значит «какое именно целое число 2?»):

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

С дробными числами происходит то же самое. Здесь число сначала сохраняется в переменную, поэтому rustc предлагает указать тип у переменной (комментарий значит «x — это f32 или f64?»):

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;
| +++++
fn main() {
let x = 2.0;
let root = x.sqrt(); // ❌ is `x` an f32 or an f64?
println!("{}", root);
}

{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 должен знать тип в этом месте кода, а здесь он его не знает.

Это как спросить продавца: «Сколько стоят две?» Две чего? Два яблока и два телевизора стоят очень по-разному. Как только скажешь «два яблока», ответить легко.

У Rust есть числовые типы по умолчанию. Если ничто другое не решает, целое число становится i32, а дробное — f64. Смотри Целые числа и Числа с плавающей точкой.

Но это значение по умолчанию — крайняя мера. Сначала Rust читает остальную функцию, чтобы проверить, не выбирает ли тип что-то ещё, например передача числа в функцию, которая принимает u8. И только в самом конце откатывается к i32 или f64. Вызов метода не может ждать так долго: Rust должен знать, какой pow он вызывает, прежде чем сможет проверить аргументы и тип результата. Поэтому вместо того чтобы гадать, он останавливается и спрашивает тебя.

Это значит ещё и то, что код дальше не поможет. Даже let y: u64 = x.pow(10); даёт E0689, потому что Rust должен выбрать тип x до вызова pow, а u64 стоит только у результата. Помогает только одно: дать числу тип до вызова метода.

Исправление 1: добавь к числу суффикс типа

Заголовок раздела «Исправление 1: добавь к числу суффикс типа»

Выбирай этот вариант, когда вызываешь метод прямо у литерала, например 2.pow(10) или 2.0.sqrt(). Напиши тип сразу после числа. _ необязателен и нужен только для читаемости: 2_i32 и 2i32 — одно и то же.

Для дробных чисел rustc предлагает f32, но f64 — тип по умолчанию в Rust, и он точнее. Бери 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;. Именно это советует строка help: от rustc. Добавь : тип после имени переменной:

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!("{} градусов от нуля", temperature.abs());
let base: u64 = 2;
let y = base.pow(10); // y — тоже u64
println!("{}", y);
}

Вывод:

1.414
площадь = 12.25
5 градусов от нуля
1024
▶ Попробуй сам

Суффикс у значения работает точно так же: let x = 2.0_f64;. Выбирай то, что тебе легче читать. Смотри Аннотации типов.

Исправление 3: укажи тип переменной цикла или аргумента замыкания

Заголовок раздела «Исправление 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));
| ^^^

Поставь суффикс на один из концов диапазона или укажи тип аргумента замыкания:

fn main() {
for i in 1..=3_u32 { // ✅ теперь весь диапазон — u32
println!("{} в квадрате = {}", 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!("сумма квадратов: {} и {}", total, total2);
}

Вывод:

1 в квадрате = 1
2 в квадрате = 4
3 в квадрате = 9
сумма квадратов: 55 и 55
▶ Попробуй сам

Обрати внимание: одного let total: u32 = ... недостаточно. rustc нужен тип n внутри замыкания, ещё до того, как дело дойдёт до .sum(). Один суффикс у диапазона исправляет всё.

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}`, потому что 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