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

Read this post in other languages:

Rust 컴파일러는 까다로운 녀석입니다. 제공된 소스 코드가 마음에 들지 않으면 컴파일러가 400개 이상의 다양한 오류를 전송할 수 있습니다(매달 더 많은 오류가 추가됩니다!). 일부 오류는 극히 드물게 발생하지만 다른 오류는 일상적으로 Rust 개발자를 괴롭힙니다. 이 블로그 게시물 시리즈에서는 JetBrains의 전용 Rust IDE인 RustRover에서 개발자가 직면하는 가장 일반적인 Rust 컴파일러 오류 메시지와 이를 방지할 수 있는 팁을 살펴봅니다. 먼저 ‘가장 일반적인 오류’가 실제로 무엇을 의미하는지 살펴보겠습니다.

RustRover의 사용 데이터에서 가장 일반적인 오류 찾기

모든 RustRover 사용자는 JetBrains에 익명의 사용 데이터 전송하는 옵션을 활성화할 수 있습니다. JetBrains는 이 데이터를 분석하여 다양한 사용자 패턴을 관찰하고 IDE 개선 방법에 대한 인사이트를 얻을 수 있습니다. 물론 사용자의 개인 정보를 중요하게 생각하므로 IDE를 통해 매우 제한적인 정보만 수집합니다. 예를 들어, 데이터의 어떤 부분으로도 사용자가 누구인지 알아낼 수 없습니다. 데이터가 익명화되어도 가장 자주 생성되는 오류 메시지와 같은 IDE 사용에 대한 전반적인 내용은 확인할 수 있습니다.

데이터 제공을 선택한 사용자가 IDE에서 Cargo Build 명령어를 실행한 후(예를 들어, 프로젝트 빌드가 필요한 실행 구성을 트리거하여) Rust 컴파일러가 오류를 전송할 때마다 오류 코드가 기록됩니다. 여기에는 사용자가 코드를 작성하면서 생겨나는 모든 코드 문제가 포함되지 않고 프로젝트를 빌드한 후에 남아 있는 문제만 포함됩니다. 중간 오류는 IDE가 제공하는 검사 및 빠른 수정 기능을 활용하여 수정되는 경우가 많습니다. 더 많은 사용자가 사용 데이터를 보내고 RustRover를 더 자주 사용할수록 JetBrains는 사용자의 경험을 더 잘 이해할 수 있고 IDE의 코드 지원 기능을 더 많이 향상시킬 수 있습니다. 따라서 데이터 전송에 동의하고 이를 통해 RustRover를 모두에게 더욱 유익하게 만드는 데 도움을 주시는 모든 분들께 감사드립니다!

JetBrains는 RustRover를 실행하는 사용자로부터 오류 코드를 수집하고 얼마나 많은 사용자에게 해당 오류가 발생했는지에 따라 오류 순위를 매겼습니다. 이 시리즈 파트에서는 가장 일반적인 오류 10위부터 6위까지를 알아보고, 다음 파트에서는 가장 일반적인 상위 5개의 오류를 공개할 예정입니다. 그리고 이러한 오류의 원인과 간단한 예를 살펴보고 문제를 해결할 수 있는 가능한 방법에 대해 논의하겠습니다.

일반적인 오류 #10: E0412(사용된 타입 이름이 범위를 벗어났습니다)

Rust는 타입 선언 위치와 타입 이름 사용 위치를 엄격하게 구분합니다. 모든 타입 이름(제네릭 타입 포함)은 어딘가에서 선언되어야 하며 사용되는 범위에 있어야 합니다. 컴파일러가 타입 이름의 사용 위치를 발견했지만 해당하는 선언 위치에 대한 정보가 없으면 E0412가 표시됩니다. RustRover 사용자의 약 12%가 이 오류를 한 번 이상은 경험했습니다.

i32 대신 i42를 입력했다고 가정해 보겠습니다. RustRover는 문제를 확인하고, 알 수 없는 타입 이름을 강조 표시합니다. 컴파일러는 세부 내용을 제공하고 수정 사항을 제안합니다. 컴파일러 출력에서 간단히 Apply fix(수정 사항 적용) 버튼을 클릭하여 이를 적용할 수 있습니다.

E0412가 발생하는 다른 상황은 다음과 같습니다.

  • 타입 선언을 잊어버림
  • 타입을 현재 범위로 가져옴
  • 제네릭 타입 이름을 삽입하여 컴파일러가 해당 타입에 액세스할 수 없도록 만듬

문제를 해결하려면 타입 선언을 제공하거나(구조체를 선언하거나 제네릭 타입 이름을 적절하게 삽입) 타입을 범위 내로 가져옵니다(use 절 사용). 공식 E0412 오류 설명에 이 오류에 대한 더 많은 예가 나와 있습니다.

일반적 오류 #9: E0061(함수 호출 시 잘못된 수의 인수가 전달되었습니다)

RustRover는 이 특정 오류를 인식하고 일련의 수정 사항을 제공하지만 RustRover 사용자의 13%는 프로젝트를 빌드하기 전에 조치를 취하지 않습니다.

오류 자체는 이해하기 어렵지 않습니다. 현재 범위에서 선언되었거나 다른 곳에서 가져온 함수가 있고 호출 위치에서 너무 적거나 너무 많은 인수를 제공하는 경우입니다. 예를 살펴보고 RustRover의 제안을 Rust 컴파일러의 제안과 비교해 보겠습니다.

이 예에서는 요즘 많은 사람들이 하는 작업인 파일 열기를 수행합니다. 다른 프로그래밍 언어로 코딩하는 데 익숙하다면 Rust에서 이 메서드는 하나의 인수만 취한다는 사실은 잊어버리고 두 번째 인수를 제공할 수도 있습니다. RustRover와 Rust 컴파일러는 모두 두 번째 인수를 제거할 것을 제안합니다. 프로젝트를 빌드하지 않아도 IDE로부터 유용한 제안을 얻을 수 있다는 점이 좋습니다. 코드에 있는 빨간색 물결선에 주목하세요. 이 선은 보통 문제가 있는 곳에 표시됩니다.

호출하는 함수가 자신의 코드에 정의되어 있다면 상황은 더욱 흥미로워집니다. 동일한 코드 예시에 계속해서 추가한다고 가정해 보겠습니다.

이 경우 RustRover는 첫 번째 대안으로 함수에 매개변수를 추가할 것을 제안하는데, 이는 실제로 여기서 합리적 해법일 수 있습니다. 그러나 Rust 컴파일러는 이를 제거할 것을 주장합니다. 이러한 차이에는 이유가 있습니다. 컴파일러가 해야 할 일은 프로그램에 문제가 없도록 하는 것이며, 이를 달성하는 가장 쉬운 방법은 호출 위치에서 추가 인수를 제거하는 것입니다. 그러나 IDE의 목표는 사용자가 달성하려는 결과에 더 가까이 다가가는 것입니다. 자신의 함수에 해당 인수를 입력했다면 그렇게 의도했을 가능성이 높으므로 RustRover는 시작한 작업을 완료하도록 도와줍니다.

일반적인 오류 #8: E0282(컴파일러가 타입을 추론할 수 없어 타입 어노테이션을 요청했습니다)

때로 컴파일러가 당황할 수 있습니다. 변수에 필요한 타입을 파악할 수 없어 타입 어노테이션을 수동으로 추가하도록 제안만 할 수 있습니다. 이 오류가 발생한 적이 있다면 동일한 상황을 겪은 13.5%의 RustRover 사용자에 속한 겁니다.

E0282와 같은 오류의 주요 원인은 일반성입니다. 많은 라이브러리 함수는 일반 타입 매개변수를 사용하지만, 컴파일러는 이러한 매개변수를 구체적인 타입으로 인스턴스화해야 하므로 혼동됩니다. 다음 예시를 살펴보죠.

여기서는 문자열의 숫자를 일부 컨테이너로 수집하려고 합니다. 안타깝게도, 컴파일러는 숫자가 어떤 타입인지, 어떤 종류의 컨테이너인지 확인할 수 없습니다.

컴파일러는 컨테이너의 타입을 먼저 지정하도록 제안합니다. 하지만 이 수정 사항을 적용하면 str::parse와 관련하여 같은 종류의 오류가 다시 발생하게 됩니다. collectparse는 모두 제네릭 메서드이지만 이를 사용하는 코드를 컴파일링하려면 컴파일러가 정확한 타입을 알아야 합니다. RustRover는 어떤 오류도 강조 표시하지 않습니다. JetBrains는 타입을 보다 원활하게 파악할 수 있도록 타입 검사 기능의 완성도를 높이기 위해 계속 노력하고 있습니다.

타입 어노테이션을 추가할 수 있는 위치는 여러 개이므로 문제를 해결하는 방법에는 여러 가지가 있습니다. numbers 벡터의 구체적인 타입을 지정할 수 있습니다.

let numbers: Vec = "1  5     6   3"

또는 collect를 호출할 때 동일한 타입을 언급할 수도 있습니다.

   .collect::<Vec>();

마지막으로, 여러 위치에서 여러 타입을 언급할 수 있습니다.

let numbers = "1  5     6   3"
   .split_whitespace()
   .map(str::parse::)
   .map(Result::unwrap)
   .collect::<Vec>();

이 오류는 쉽게 해결할 수 있습니다. 원하는 타입(하나 또는 여러 개)을 지정하기만 하면 됩니다.

일반적인 오류 #7: E0432(가져오기가 확인되지 않았습니다)

RustRover는 다양한 자동 완성 기능을 제공합니다. 예를 들어, 코드에 정규식을 삽입하는 것부터 시작해 보겠습니다.

첫 번째 제안을 선택하면 코드 완성 자체 외에 두 가지 상황이 더 발생하게 됩니다.

  • regex 크레이트에 대한 종속성이 Cargo.toml에 추가됩니다.
  • use regex::Regex; 절이 파일 상단에 추가됩니다.

이와 같은 use 절을 추가하면 가져오기가 자동으로 올바르게 작성됩니다. 하지만 가져오기를 수동으로 작성해야 할 경우가 있는데, 이 때 E0432 오류가 발생할 수 있습니다. RustRover 사용자의 15.5%가 때때로 이러한 현상을 경험하며, 주로 크레이트나 모듈 이름의 철자를 틀리거나, 존재하지 않는 대상을 가져오려고 하거나, 어딘가에서 복사하여 붙여 넣은 후 잘못된 use 절이 코드에 몰래 들어가는 경우가 그렇습니다. 가장 우선적인 제안은 항상 종속성과 이름을 확인하라는 것입니다.

때로 RustRover가 이 오류를 방지하는 데 도움을 줄 수 있습니다. 가져오려는 크레이트를 알고 있는 경우 RustRover는 외부 소스에서 코드를 붙여 넣을 때 종속성을 추가하라고 제안하거나 다음 빠른 수정을 통해 지원을 제공할 수 있습니다.

Cargo.toml에 해당 종속성을 추가하면 오류가 즉시 수정됩니다. 크레이트를 사용할 수 있게 되면 이름과 관련된 추가적인 문제를 피하기 위해 use 절에서 다른 경로 요소에 대해 자동 완성을 사용하는 것이 더 안전합니다. 또한 일부 이름의 사용 가능 여부는 크레이트의 활성화된 기능에 따라 달라질 수 있다는 점을 기억하세요.

super 또는 crate와 같은 특수 경로 이름에 문제가 있을 수도 있습니다. 이는 특히 Rust 버전마다 처리하는 방식이 다르다는 점에서 그렇습니다. 이에 대한 자세한 내용은 공식 설명을 참조하세요.

일반적인 오류 #6: E0382(변수의 내용이 다른 곳으로 이동된 후 변수가 사용되었습니다)

이는 소유권과 관련된 흥미로운 문제입니다. RustRover 사용자 중 17%가 이 오류를 경험했습니다. 공식 설명에 많은 예시와 함께 자세한 내용이 설명되어 있습니다. 안타깝게도, RustRover는 여기서 큰 도움이 되지 않습니다. 외부 linter가 비활성화된 경우, RustRover의 내부 메커니즘은 다음 코드에서 어떤 문제도 발견하지 못합니다.

fn main() {
   let vec = vec![1, 2, 3, 4, 5];
   let mut sum = 0;
   for v in vec {
       sum += v;
   }
   println!("Sum of {vec:?} elements is {sum}");
}

이 순수해 보이는 코드는 다른 몇몇 프로그래밍 언어에서는 전혀 문제될 것이 없을 것입니다. 하나의 벡터가 있고 그 요소의 합을 계산하려고 합니다. 예를 들어, C를 사용해 왔고 이터레이터를 사용할 때의 멋진 기능적 프로그래밍 비법을 전혀 모른다면, 대신 전통적인 for 루프를 작성합니다. 모든 준비가 끝나면 간단히 벡터와 계산 결과를 출력합니다. 그렇죠?

하지만 Rust에서는 이렇게 하면 안 됩니다. Rust에는 소유권 규칙이라는 것이 있기 때문입니다.

문제는 for 루프의 데이터 소스가 전체 벡터의 소유권을 가져오는 into_iter() 호출로 확장된다는 것입니다. 따라서 println!에서 벡터의 요소에 액세스하려고 할 때 컴파일러는 벡터가 이동되었다고 문제를 제기합니다.

이때 컴파일러는 쉬운 해결책을 제안합니다. 즉, &vec을 반복하여 루프로 이동하지 말고, 대신 빌리라는 것입니다.

일반적으로, 항상 값의 소유권을 추적하면 도움이 됩니다. 값의 이동과 빌리기는 Rust의 근간이 되는 개념입니다. 이를 이해하는 것이 모든 학습자가 최우선적으로 해야 할 일입니다.

요약

블로그 게시물 시리즈의 이번 첫 파트에서는 RustRover에서 얻을 수 있는 사용 데이터를 기반으로 가장 일반적인 Rust 컴파일러 오류를 정의하고 10위부터 6위까지의 오류에 대해 논의했습니다. 다음 파트에서는 가장 많이 발생하는 상위 5개 오류를 살펴보고 모든 Rust 개발자들이 궁금해 하는 “Rust에서 가장 성가신 문제는 무엇인가?”라는 질문에 답해 보겠습니다.

게시물 원문 작성자

Jessie Cho

Vitaly Bragilevsky

image description

Discover more