RustRover에서 발생하는 가장 일반적인 Rust 컴파일러 오류: 파트 2

Read this post in other languages:

이 블로그 시리즈 전반부에서는 RustRover 사용 데이터로부터 도출한 가장 일반적인 Rust 컴파일러 오류 10가지 중 절반까지 살펴보았습니다. 이번에는 가장 자주 발생하는 상위 5가지 오류를 살펴본 다음, 컴파일러 사용 중 개발자를 가장 ‘당황’하게 만드는 Rust 프로그래밍 언어의 측면을 좀 더 폭넓게 알아보겠습니다.

일반적인 오류 #5: E0433(An undeclared crate, module, or type was used(선언되지 않은 크레이트, 모듈 또는 타입이 사용되었습니다))

이 오류는 전반부에서 논의한 E0432(가져오기가 확인되지 않았습니다)와 비슷합니다. 유일한 차이점은 문제의 구성 요소가 있는 경로가 use 절 없이 이름에 직접 사용된다는 것입니다. RustRover 사용자 중 17.5%가 이 오류를 경험했습니다. 예를 들어, 아래 코드에서 컴파일러는 원래 의도(크레이트 또는 모듈)를 파악하지 못하고 다음과 같은 문제가 있다고 나타냅니다.

이 예를 보면 Rust 컴파일러가 오류 메시지로 널리 칭찬받는 이유를 알 수 있습니다. 메시지를 읽지 않아도 무엇을 수정해야 할지 분명합니다. 이름을 확인하고, 경우에 따라 Cargo.toml에 필요한 종속성을 추가하거나 적절한 가져오기를 삽입하면 됩니다.

RustRover도 가져온 이름을 선택할 수 있다는 점에서 유용합니다.

코드를 작성하는 동안 코드 완성을 사용하면 이 오류를 완전히 피할 수 있습니다.

일반적인 오류 #4: E0425(An unresolved name was used(해결되지 않은 이름이 사용되었습니다))

또 다른 ‘미해결’ 오류로 미지의 문제를 계속 살펴보겠습니다. RustRover 사용자의 20.5%는 구문적으로 크레이트나 모듈 이름이 아닌 미해결 이름을 사용해본 경험이 있습니다. 이 오류가 발생하면 다음과 같은 내용이 나타납니다.

물론, 이런 일이 실제로 발생하면 위 이미지처럼 명확하지 않을 가능성이 높습니다.

뭐든 해줄 것 같은 Rust 컴파일러도 이 오류에 대해서는 많은 도움을 주지 못합니다. 몇 가지 취할 수 있는 조치가 있습니다. 이름을 수정하거나, 정의를 제공하거나, 적절한 가져오기를 삽입하는 것입니다.

일반적인 오류 #3: E0599(A method is used on a type which doesn’t implement it(해당 메서드가 구현되지 않은 타입에서 메서드가 사용됩니다))

내용을 미리 밝혀 김이 샐 수도 있지만, 지금부터 다룰 내용은 순수 타입 검사 오류입니다. 그렇습니다. 상위 3개는 모두 잘못된 타입의 사용과 관련된 오류들입니다. 지금 이 오류는 해당 메서드를 구현하지 않는 타입의 값에 대해 메서드를 호출하려고 시도했을 때 나타나며 RustRover 사용자의 27.5%가 경험했습니다. 다음 예를 살펴보겠습니다.

fn main() {
   let numbers: Vec = vec![4, 11, 15, 12];
   let sum: usize = numbers.sum();
   println!("Answer = {sum}");
}

Vec에 대한 sum 메서드가 없다는 점을 제외하면 문제될 것이 없어 보입니다. 구체적으로 오류 메시지의 내용은 다음과 같습니다.

error[E0599]: `Vec` is not an iterator

이 경우에 해결 방법은 먼저 iter 메서드를 호출한 다음, 결과적인 반복자에 sum을 사용하는 것입니다.

한 가지 덧붙이자면, RustRover에는 이와 같은 오류를 방지하는 기능이 있습니다. 이를 체인 완성이라고 합니다. 다음 예만 보아도 실제 어떤 이점이 있는지 확실히 알 수 있습니다.

완성 제안이 적용되면 itersum 메서드가 모두 호출 체인에 추가되어 코드가 즉시 수정됩니다.

일반적으로 코드 완성을 사용하면 E0599 메시지가 효과적으로 방지됩니다. 필요한 메서드 호출에 대한 제안이 표시되지 않으면 피호출자 타입이 잘못되었기 때문일 수 있습니다.

여기서 한 가지 중요하게 언급할 부분은, 많은 상황에서 자신의 타입 선언을 제어할 수 있다는 것입니다. 한 가지 방법은 먼저 메서드를 호출한 다음, Alt-Enter(⌥OptionReturn)를 누르고 제안된 액션을 실행하여 RustRover에게 구현에 스캐폴드를 적용하도록 요청하는 것입니다.

이 액션을 실행하면 RustRover가 타입에 관한 모든 컨텍스트 정보를 고려하여 메서드 구현 템플릿을 생성합니다.

impl Info {
   pub(crate) fn summary(&self) -> String {
       todo!()
   }
}

이 코드를 실행하면 런타임 오류가 발생하지만 적어도 컴파일러는 더 이상 문제가 있다고 나타내지 않습니다.

일반적인 오류 #2: E0308(Expected type did not match the received type(필요한 타입이 수신된 타입과 일치하지 않습니다))

B 타입의 값이 필요한 컨텍스트에서 A 타입의 표현식을 사용한다고 생각해 보겠습니다. 여기서 ‘컨텍스트’는 호출 위치의 함수 매개변수, 변수 선언, 제어 흐름 구문/표현식 등을 의미할 수 있습니다. RustRover 사용자의 30%가 이러한 상황을 경험했습니다.

개념적으로 이 경우에는 값을 조정하거나 컨텍스트를 조정하는 두 가지 선택이 있습니다. 값을 조정하려면 형 변환, 참조/역참조, 변환 메서드 호출 또는 다른 대상으로 바꾸기 등의 방법을 사용할 수 있습니다. 컨텍스트 조정은 항상 가능한 것은 아니지만 수신된 타입과 일치하도록 필요한 타입을 변경할 수 있는 경우가 있습니다.

RustRover는 다음과 같은 빠른 수정 옵션을 제공하여 두 가지 작업 모드를 모두 지원합니다.

목록의 두 번째 제안에 주목하세요. 이 경우 RustRover는 컴파일러에 적합한 값을 매우 정교하게 변환했습니다. 첫 번째 제안을 따르면 대신 컨텍스트, 즉 함수 정의가 수정됩니다.

일반적인 오류 #1: E0277(You tried to use a type which doesn’t implement some trait in a place which expected that trait(특성이 필요한 위치에 어떤 특성을 구현하지 않는 타입을 사용하려고 했습니다))

이제 대망의 1위까지 왔습니다! RustRover에서 발생하는 가장 일반적인 Rust 컴파일러 오류는 E0277입니다. 개발자에게 악몽과도 같은 이러한 타입과 특성 관련 오류는 RustRover 사용자의 32%가 경험했습니다. 이 경우에도 공식 웹사이트의 설명을 통해 사례와 가능한 수정 사항을 잘 이해할 수 있습니다. 대신 RustRover의 동작에 집중해 보겠습니다.

이런 종류의 오류를 처리하는 데 제가 즐겨 사용하는 RustRover 기능은 다음과 같습니다.

컴파일러는 다음과 같은 문제가 있다고 알려줍니다.

error[E0277]: `Info` doesn't implement `std::fmt::Display`

물론 Display 특성을 구현하는 것은 가능한 선택 중 하나이며 RustRover는 구현에 스캐폴드를 적용하려고 합니다. 하지만 이 경우에는 두 가지 동시 단계가 포함된 첫 번째 제안을 적용하려고 합니다.

  • Debug 특성에 대한 구현 파생
  • Debug 특성의 서식 지정 기능을 호출하도록 서식 문자열 변경

안타깝게도 다른 많은 경우에는 RustRover가 이 오류를 자체적으로 발견하고 강조 표시하지 못합니다. 전반부에서 이미 언급했듯이 타입 검사 기능은 아직 그렇게 강력하지는 않으며 개선을 위해 노력하고 있습니다. E0277은 가장 일반적으로 발생하는 오류 메시지인 만큼 확실한 대책이 나올 것입니다.

일반적인 Rust 오류에 대한 고찰

시야를 더 넓혀보겠습니다. 저희는 몇 주전에, X(이전의 Twitter) 커뮤니티에 Rust 프로그래밍 언어의 어떤 측면이 코드 오류의 주요 원인인지 물었습니다. 이에 대한 응답은 다음과 같습니다.

커뮤니티의 답변을 저희 데이터에서 얻은 자료와 비교하기 위해 가장 일반적인 Rust 컴파일러 오류 상위 25개를 살펴보고 이를 대략 5가지 카테고리로 분류했습니다.

  • 타입과 특성
  • 소유권과 유효 기간
  • 매크로
  • 해결되지 않은 이름 또는 존재하지 않는 요소
  • 기타

결과는 다음과 같습니다(오류 코드 번호를 기준으로 오름차순).

오류 코드 설명 카테고리
E0061 An invalid number of arguments was passed when calling a function(함수 호출 시 잘못된 수의 인수가 전달되었습니다) 기타
E0063 A struct’s or struct-like enum variant’s field was not provided(구조체 또는 구조체와 유사한 열거형 배리언트의 필드가 제공되지 않았습니다) 해결되지 않음/존재하지 않음
E0106 This error indicates that a lifetime is missing from a type(이 오류는 타입에 유효 기간이 누락되었음을 나타냅니다) 소유권과 유효 기간
E0107 An incorrect number of generic arguments was provided(잘못된 수의 제네릭 인수가 제공되었습니다) 타입과 특성
E0277 You tried to use a type which doesn’t implement some trait in a place which expected that trait(필요한 위치에 어떤 특성을 구현하지 않는 타입을 사용하려고 했습니다) 타입과 특성
E0282 The compiler could not infer a type and asked for a type annotation(컴파일러가 타입을 추론할 수 없어 타입 어노테이션을 요청했습니다) 타입과 특성
E0283 An implementation cannot be chosen unambiguously because of lack of information(정보가 부족하기 때문에 구현을 명확하게 선택할 수 없습니다) 타입과 특성
E0308 Expected type did not match the received type(필요한 타입이 수신된 타입과 일치하지 않습니다) 타입과 특성
E0369 A binary operation was attempted on a type which doesn’t support it(2진 연산을 지원하지 않는 타입에서 2진 연산을 시도했습니다) 타입과 특성
E0382 A variable was used after its contents have been moved elsewhere(변수의 내용이 다른 곳으로 이동된 후 변수가 사용되었습니다) 소유권과 유효 기간
E0412 A used type name is not in scope(사용된 타입 이름이 범위 내에 없습니다) 해결되지 않음/존재하지 않음
E0423 An identifier was used like a function name or a value was expected and the identifier exists but it belongs to a different namespace(식별자가 함수 이름처럼 사용되었거나 값이 필요하며, 식별자가 있지만 다른 네임스페이스에 속합니다) 해결되지 않음/존재하지 않음
E0425 An unresolved name was used(해결되지 않은 이름이 사용되었습니다) 해결되지 않음/존재하지 않음
E0432 An import was unresolved(가져오기가 확인되지 않았습니다) 해결되지 않음/존재하지 않음
E0433 선언되지 않은 크레이트, 모듈 또는 타입이 사용되었습니다. 해결되지 않음/존재하지 않음
E0502 A variable already borrowed as immutable was borrowed as mutable(이미 불변으로 차용된 변수가 가변으로 차용되었습니다) 소유권과 유효 기간
E0507 A borrowed value was moved out(차용된 값이 옮겨졌습니다) 소유권과 유효 기간
E0515 A reference to a local variable was returned(지역 변수에 대한 참조가 반환되었습니다) 소유권과 유효 기간
E0596 This error occurs because you tried to mutably borrow a non-mutable variable(이 오류는 불변 변수를 가변으로 차용하려고 했기 때문에 발생합니다) 소유권과 유효 기간
E0597 This error occurs because a value was dropped while it was still borrowed(이 오류는 값이 차용된 동안 삭제되었기 때문에 발생합니다) 소유권과 유효 기간
E0599 This error occurs when a method is used on a type which doesn’t implement it(이 오류는 해당 메서드가 구현되지 않은 타입에서 메서드가 사용될 때 발생합니다) 타입과 특성
E0609 Attempted to access a nonexistent field in a struct(구조체에 존재하지 않는 필드에 액세스하려고 했습니다) 해결되지 않음/존재하지 않음
E0614 역참조할 수 없는 변수를 역참조하려고 했습니다. 기타
E0658 불안정한 기능이 사용되었습니다. 기타
E0716 차용이 아직 활성 상태인 동안 임시 값이 삭제됩니다. 소유권과 유효 기간

안타깝게도 저희가 보유한 데이터로는 매크로에 관해 많은 것을 알아낼 수 없었습니다. 매크로 확장 문제를 안정적으로 감지할 수 없을 뿐만 아니라, 성공적으로 확장된 매크로에서 유래하는 다른 오류를 식별할 수도 없습니다. 어쩌면 이는 수집된 데이터를 더욱 세분화하여 분류해야 할 필요가 있음을 말해주는 것일 수 있습니다.

매크로를 무시하면, 상위 25개의 일반적인 오류 중에서 확실한 승자는 없는 것 같습니다. 이는 예외 없이 모든 Rust 부분이 만족할 만하다는 의미일 수 있습니다.

  • 타입과 특성 – 7개 오류
  • 소유권과 유효 기간 – 8개 오류
  • 해결되지 않은 이름 또는 존재하지 않는 요소 – 7개 오류
  • 기타 – 3개 오류

요약

이 글에서는 RustRover에서 발생하는 가장 일반적인 Rust 컴파일러 오류 5가지를 살펴보았습니다. 이 중 3가지는 타입 및 특성과 관련된 것으로, 이 범주의 오류가 Rust 코드를 올바르게 작성하는 데 다소 중요하다는 점을 시사합니다. 이러한 결론은 가장 일반적인 25가지 오류에 관한 보다 일반적인 데이터를 관찰하여 도출되었으며 Rust의 다른 측면도 자주 접하는 오류에 영향을 미친다는 사실이 확인되었습니다.

게시물 원문 작성자

Jessie Cho

Vitaly Bragilevsky

image description

Discover more