Rust 에러 E0614: type cannot be dereferenced (역참조할 수 없는 타입)
E0614는 참조자가 아닌 것 앞에 *를 써서, 따라갈 곳이 없다는 뜻이에요. * 연산자는 “이 참조자가 가리키는 값으로 가라”는 뜻이에요. 평범한 u32, i32, 구조체는 어디도 가리키지 않아요. 그 자체가 값이에요. 주로 클로저와 반복문에서 생기는데, 지금 가진 게 &u32인지 u32인지 놓치기 쉬운 곳이거든요. rustc는 제목 줄에서 진짜 타입을 알려 주고, 해결은 거의 언제나 * 하나를 지우는 거예요. 이 페이지는 Rust 컴파일러 에러 해설의 일부예요.
에러 메시지
섹션 제목: “에러 메시지”error[E0614]: type `u32` cannot be dereferenced --> src/main.rs:3:64 |3 | let doubled: Vec<u32> = visits.iter().copied().map(|count| *count * 2).collect(); // ❌ `count`는 이미 `u32`예요 | ^^^^^^ can't be dereferenced이 에러를 내는 프로그램은 다음과 같아요.
fn main() { let visits: Vec<u32> = vec![12, 0, 7, 30]; let doubled: Vec<u32> = visits.iter().copied().map(|count| *count * 2).collect(); // ❌ `count`는 이미 `u32`예요 println!("{:?}", doubled);}무슨 뜻인가요
섹션 제목: “무슨 뜻인가요”^^^^^^는 3번째 줄의 *count를 가리키고, “can’t be dereferenced”(역참조할 수 없다)라는 라벨이 붙어 있어요. 제목 줄은 type `u32` cannot be dereferenced(타입 u32는 역참조할 수 없다)라고, count가 실제로 무엇인지 알려 줘요. &u32가 아니라 u32예요.
체인을 따라 타입을 쫓아가 보세요.
visits.iter()는 항목의 참조자를 줘요.&u32,&u32, ….copied()는 각&u32를 평범한u32복사본으로 바꿔요. 참조자는 사라졌어요.- 그래서
.map(|count| ...)에서count는u32이고,*count는 존재하지 않는 포인터를 따라가라고 Rust에게 요청하는 거예요.
제목 줄이 type `{integer}` cannot be dereferenced라고 해도 같은 경우예요. {integer}는 “아직 정하지 않은 어떤 정수 타입”을 rustc가 쓰는 방식이에요. 보통 타입을 따로 정해 주지 않은 72 같은 숫자예요.
누군가에게 “이 주소에 뭐가 있어요?“라고 물으면서, 주소가 적힌 쪽지 대신 택배 상자를 건넨 것과 비슷해요. 그 사람은 이미 물건을 갖고 있어요. 찾아갈 주소가 없어요.
Rust가 이렇게 하는 이유
섹션 제목: “Rust가 이렇게 하는 이유”Rust에서 참조자(&T)와 값 자체(T)는 서로 다른 타입이에요. *는 어딘가를 가리키는 것에만 쓸 수 있어요. 참조자 &T와 &mut T, Box<T>와 Rc<T> 같은 스마트 포인터, 그리고 Deref 트레이트를 구현한 타입이요. u32나 평범한 구조체에는 그런 게 없으니, 거기에 *를 쓰는 건 아무 의미가 없어요.
Rust가 남는 *를 조용히 무시할 수도 있겠지만, 그러지 않아요. 남는 *는 대개 내가 가진 타입을 잘못 알고 있다는 뜻이고, 그런 혼동은 다른 버그로 이어지기 쉬워요. 엉뚱한 것끼리 비교하거나, 빌리려던 값을 이동시켜 버리는 식으로요. 그래서 rustc는 멈추고 진짜 타입을 알려 줘요. 대여와 스마트 포인터를 참고하세요.
반대 실수에는 다른 에러가 있어요. 참조자에 *를 빠뜨리면, 예를 들어 count가 &u32인데 count > 10이라고 쓰면, E0308 타입 불일치(mismatched types)가 나요.
고치는 방법
섹션 제목: “고치는 방법”방법 1: * 빼기
섹션 제목: “방법 1: * 빼기”거의 언제나 이 방법을 고르세요. 특히 .copied(), .cloned(), Vec의 .into_iter()처럼 값을 건네주는 것 뒤에서요. 값이 이미 거기 있으니, 그냥 쓰면 돼요.
fn main() { let visits: Vec<u32> = vec![12, 0, 7, 30]; let doubled: Vec<u32> = visits.iter().copied().map(|count| count * 2).collect(); println!("{:?}", doubled);
// `filter`는 각 항목을 클로저에 빌려줘요. 그래서 여기서 `count`는 `&u32`가 맞고, // `*`도 맞아요. let busy: Vec<u32> = visits.iter().copied().filter(|count| *count > 10).collect(); println!("{:?}", busy);}실행 결과:
[24, 0, 14, 60][12, 30]두 번째 부분에서 많은 사람이 헷갈려요. 같은 체인 안에서도 map은 항목 자체를 받고, filter는 항목의 참조자를 받아요. 헷갈리면 문서에서 메서드를 확인하세요. filter는 &가 붙은 FnMut(&Self::Item) -> bool을 받아요. 반복자와 클로저를 참고하세요.
방법 2: for 반복문에서 값과 참조자 중 고르기
섹션 제목: “방법 2: for 반복문에서 값과 참조자 중 고르기”*가 for 반복문 안에 있을 때 이 방법을 고르세요. for score in scores는 Vec를 분해해서 각 값을 주기 때문에 *score는 실패해요.
error[E0614]: type `{integer}` cannot be dereferenced --> src/main.rs:5:18 |5 | total += *score; // ❌ `score`는 참조자가 아니라 숫자예요 | ^^^^^^ can't be dereferenced문제가 되는 프로그램은 이래요.
fn main() { let scores = vec![72, 95, 88]; let mut total = 0; for score in scores { total += *score; // ❌ `score`는 참조자가 아니라 숫자예요 } println!("합계: {}", total);}*를 빼거나, &scores를 돌면 돼요. 그러면 각 score는 &i32가 되고, *가 맞게 되고, 반복문이 끝난 뒤에도 Vec를 쓸 수 있어요.
fn main() { let scores = vec![72, 95, 88];
let mut total = 0; for score in &scores { total += *score; // 이제 `score`는 `&i32`예요 } println!("합계: {}", total);
// `scores`는 반복문으로 이동하지 않았으니, 아직 여기 있어요. println!("점수 {}개, 평균 {}", scores.len(), total / scores.len() as i32);}실행 결과:
합계: 255점수 3개, 평균 85간단한 규칙이에요. for x in v는 값을, for x in &v는 & 참조자를, for x in &mut v는 &mut 참조자를 줘요. *가 필요한 건 뒤의 두 경우뿐이에요. for 반복문과 벡터를 참고하세요.
방법 3: &x로 참조자를 매칭하거나 *x를 쓰기, 둘 다는 말고
섹션 제목: “방법 3: &x로 참조자를 매칭하거나 *x를 쓰기, 둘 다는 말고”클로저나 반복문의 패턴에 이미 |&p|처럼 &가 있을 때 이 방법을 고르세요. 패턴 안의 &는 참조자를 대신 풀어 줘요. 그래서 p는 평범한 숫자이고, *p는 한 단계 더 간 거예요.
error[E0614]: type `{integer}` cannot be dereferenced --> src/main.rs:3:53 |3 | let with_fee: Vec<i32> = prices.iter().map(|&p| *p + 1).collect(); // ❌ `&p`가 이미 `&`를 없앴어요 | ^^ can't be dereferenced문제가 되는 프로그램은 이래요.
fn main() { let prices = [3, 5, 8]; let with_fee: Vec<i32> = prices.iter().map(|&p| *p + 1).collect(); // ❌ `&p`가 이미 `&`를 없앴어요 println!("{:?}", with_fee);}참조자를 없애는 방법을 하나만 고르세요. 아래 두 줄은 같은 일을 해요.
fn main() { let prices = [3, 5, 8];
let with_fee: Vec<i32> = prices.iter().map(|&p| p + 1).collect(); // 패턴이 `&`를 없애요 let same: Vec<i32> = prices.iter().map(|p| *p + 1).collect(); // `*`가 `&`를 없애요 println!("{:?} {:?}", with_fee, same);
// `.iter()`에 쓴 `filter`는 `&&i32`를 받아요. `&`가 두 개라서 패턴은 `&&p`예요. let pricey = prices.iter().filter(|&&p| p > 4).count(); println!("비싼 항목 {}개", pricey);}실행 결과:
[4, 6, 9] [4, 6, 9]비싼 항목 2개반복문도 마찬가지예요. for &(name, qty) in &orders는 각 참조자를 풀어 주니까, name과 qty를 * 없이 쓰세요. 패턴 매칭을 참고하세요.
방법 4: 타입을 확인하고, 감싼 타입에는 필드나 Deref 쓰기
섹션 제목: “방법 4: 타입을 확인하고, 감싼 타입에는 필드나 Deref 쓰기”제목 줄에 type `Meters` cannot be dereferenced처럼 내가 만든 타입이 나올 때 이 방법을 고르세요. 값 하나만 감싸고 있더라도, 구조체는 참조자가 아니에요.
error[E0614]: type `Meters` cannot be dereferenced --> src/main.rs:5:18 |5 | let longer = *run + 1.0; // ❌ `run`은 참조자가 아니라 `Meters`예요 | ^^^^ can't be dereferenced문제가 되는 프로그램은 이래요.
struct Meters(f64);
fn main() { let run = Meters(2.5); let longer = *run + 1.0; // ❌ `run`은 참조자가 아니라 `Meters`예요 println!("{} m", longer);}튜플 구조체 안의 숫자를 꺼내려면 필드 .0을 쓰세요.
struct Meters(f64);
fn main() { let run = Meters(2.5); let longer = run.0 + 1.0; println!("{} m", longer);}실행 결과:
3.5 m내 타입이 정말 스마트 포인터 같은 래퍼(wrapper)이고 *가 동작하길 원한다면, std::ops::Deref를 구현하세요. 이건 래퍼 타입에만 하세요. Meters 같은 “뉴타입”(newtype)에는 .0이나 메서드가 더 분명해요. 변수가 어떤 타입인지 모르겠다고요? 에디터에서 변수 위에 마우스를 올려 보세요(rust-analyzer가 타입을 보여 줘요). 아니면 제목 줄을 믿으세요. 언제나 진짜 타입을 알려 주니까요. 구조체와 스마트 포인터를 참고하세요.
자주 보게 되는 곳
섹션 제목: “자주 보게 되는 곳”구조체 필드에 쓴 *. config가 구조체(또는 구조체의 참조자)이고 retries가 u32일 때 *config.retries는 type `u32` cannot be dereferenced를 내요. .이 이미 참조자를 대신 따라가 줘요. 해결: config.retries라고 쓰세요.
HashMap을 값으로 도는 경우. for (word, count) in counts { total += *count; }는 type `u32` cannot be dereferenced를 내요. counts 자체를 돌면 소유된 키와 값이 나오거든요. 해결: *를 빼거나, 나중에 맵이 필요하면 &counts를 도세요. 해시맵을 참고하세요.
.copied().max()나 .cloned().find(...) 뒤. 이것들은 Option<&u32>가 아니라 Option<u32>를 줘요. 그래서 if let Some(best) = ... { *best }에서 *는 하나 더 붙은 거예요. 해결: best를 그대로 쓰세요. .copied() 없이 .iter().max()를 쓰면 Option<&u32>가 나오니, 그때는 *best가 맞아요.
관련 에러
섹션 제목: “관련 에러”- E0308: 타입 불일치예요. 반대 실수로, 참조자와 평범한 값이 만나는 곳에
*가 빠졌어요. - E0369: 이항 연산을 그 타입에 쓸 수 없어요. 참조자와 값이 섞일 때 만날 수 있는 또 다른 에러예요.
- E0507: 참조자에서 값을 이동할 수 없어요. 여기서는
*가 동작하긴 하지만,Copy가 아닌 값을 이동시키게 돼요. - E0610:
{integer}는 원시 타입이라 필드가 없어요. 이 에러의 “그냥 숫자일 뿐” 사촌이에요. - E0599: 메서드를 찾을 수 없어요. 값이 생각한 타입이 아닐 때 나요.
- 전체 컴파일러 에러: 에러 페이지 전체 목록이에요.
이해도 점검
섹션 제목: “이해도 점검”이해도 점검
1. visits.iter().copied().map(|count| *count + 1)에서 *count가 왜 E0614를 낼까요?
2. scores가 Vec<i32>일 때 for score in &scores { total += *score; }는 컴파일될까요?
3. prices.iter().map(|&p| *p * 2)가 E0614를 내요. 올바른 버전은 무엇일까요?
점수: 0 / 3
더 알아보기
섹션 제목: “더 알아보기”- E0614 — Rust error codes index (영어): 최소한의 예제와 해결책이 있는 공식 설명이에요.
- 참조와 대여 — The Rust Programming Language 한국어판:
&와*가 무슨 뜻인지, 왜 참조자와 값이 서로 다른 타입인지 알려 줘요. - Deref 트레이트로 스마트 포인터를 보통의 참조자처럼 취급하기 — The Rust Programming Language 한국어판:
*가 참조자를 어떻게 따라가는지,Deref트레이트가 내 타입에서도 그걸 어떻게 가능하게 하는지 설명해요. Iterator::copied— standard library docs (영어):&T항목을T항목으로 바꾸는 메서드로,*가 하나 더 붙게 되는 가장 흔한 이유예요.- Destructuring pointers — Rust by Example (영어): 패턴 속
&val과 표현식 속*val의 차이를 보여 줘요.