Ошибка 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 так делает
Заголовок раздела «Почему 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 = 10242^40 = 1099511627776sqrt(2) = 1.4142135623730951|-7| = 7Выбирай тип, в который результат поместится. 2_i32.pow(40) скомпилируется, но переполнится и запаникует в отладочной сборке. Смотри урок Переполнение целых чисел.
Исправление 2: укажи тип переменной
Заголовок раздела «Исправление 2: укажи тип переменной»Выбирай этот вариант, когда число сначала сохраняется в переменную, например 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.255 градусов от нуля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 в квадрате = 12 в квадрате = 43 в квадрате = 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];.
Связанные ошибки
Заголовок раздела «Связанные ошибки»- E0282: type annotations needed: Rust вообще не может вывести тип, например для
Vec::new()или.collect(). Идея исправления та же: назови тип. - E0610: primitive type doesn’t have fields:
.somethingбез()у{integer}, напримерx.absвместоx.abs(). - E0599: no method named x found: тип известен, но такого метода у него нет, или (как выше) это ссылка на
{integer}. - E0308: mismatched types: то, что может появиться следующим, если выбранный тип не подходит там, где используется число.
- Все ошибки компилятора с объяснениями
Проверь себя
Заголовок раздела «Проверь себя»Проверь себя
1. Какая строка компилируется?
2. Какой тип Rust даёт 7 и 7.0, если ничто другое не решает?
3. for i in 0..10 { println!("{}", i.pow(2)); } даёт E0689. Какое исправление самое простое?
Результат: 0 / 3
Что почитать
Заголовок раздела «Что почитать»- E0689 — Rust error codes index (на английском): официальное объяснение этой ошибки.
- Типы данных — книга «Язык программирования Rust» (перевод на русский): целые и дробные типы, числовые литералы с суффиксами вроде
57u8и типы по умолчаниюi32/f64. - Литералы и операторы — Rust на примерах (перевод на русский): как суффиксы вроде
1u32и1.0f64выбирают тип. - Integer literal expressions — The Rust Reference (на английском): точные правила того, как литерал получает тип, включая откат к
i32. f64::sqrt— стандартная библиотека Rust (на английском): методы дробных чисел (sqrt,powi,round…), которым нужен известный тип.