콘텐츠로 이동

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); // ❌ `2`는 어떤 종류의 정수일까요?
| ^^^
|
help: you must specify a concrete type for this numeric value, like `i32`
|
2 | let kb = 2_i32.pow(10); // ❌ `2`는 어떤 종류의 정수일까요?
| ++++

“can’t call method pow on ambiguous numeric type {integer}“는 “타입이 모호한 숫자 {integer}에는 pow 메서드를 호출할 수 없다”는 뜻이에요. help는 “이 숫자 값에 i32 같은 구체적인 타입을 지정해야 한다”며, ++++로 추가할 글자를 보여 줘요.

이 에러를 내는 프로그램은 다음과 같아요.

fn main() {
let kb = 2.pow(10); // ❌ `2`는 어떤 종류의 정수일까요?
println!("{}", kb);
}

소수에서도 똑같은 일이 생겨요. 아래에서는 숫자를 먼저 변수에 넣었기 때문에, rustc가 그 변수에 타입을 붙이라고 제안해요.

error[E0689]: can't call method `sqrt` on ambiguous numeric type `{float}`
--> src/main.rs:3:18
|
3 | let root = x.sqrt(); // ❌ `x`는 f32일까요, 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(); // ❌ `x`는 f32일까요, 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가 코드의 바로 그 지점에서 타입을 알아야 하는데, 여기서는 모르는 거예요.

가게 직원에게 “2개에 얼마예요?“라고 묻는 것과 비슷해요. 뭐가 2개요? 사과 2개와 TV 2대는 가격이 아주 다르죠. “사과 2개”라고 말하면 바로 답할 수 있어요.

Rust에도 기본 숫자 타입이 있긴 해요. 다른 무엇도 정해 주지 않으면 정수는 i32, 소수는 f64가 돼요. 정수와 부동소수점 숫자 (소수)를 참고하세요.

하지만 이 기본값은 최후의 수단이에요. Rust는 먼저 함수의 나머지 부분을 읽으면서 다른 무언가가 타입을 정해 주는지 살펴봐요. 예를 들어 그 숫자를 u8을 받는 함수에 넘긴다면요. 맨 마지막에야 i32나 f64로 정해요. 메서드 호출은 그렇게 오래 기다릴 수 없어요. Rust는 인자와 결과 타입을 검사하기 전에 어느 pow를 호출하는지 알아야 하거든요. 그래서 추측하는 대신 멈추고 물어보는 거예요.

이 말은 뒤에 나오는 코드는 도움이 안 된다는 뜻이기도 해요. let y: u64 = x.pow(10);조차 E0689를 내요. Rust는 pow를 호출하기 전에 x의 타입을 골라야 하는데, 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;처럼 숫자를 먼저 변수에 넣었을 때 이 방법을 고르세요. 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: 반복문 변수나 클로저 인자에 타입 주기

섹션 제목: “방법 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));
| ^^^

라벨은 “이 변수에 i32 같은 타입을 지정해야 한다”는 뜻이에요.

범위의 한쪽 끝에 접미사를 붙이거나, 클로저 인자에 타입을 주세요.

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는 .sum()에 닿기 전에, 클로저 안에서 n의 타입을 알아야 해요. 범위에 접미사 하나만 붙이면 전부 해결돼요.

음수에 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