Rust를 약 10년 동안 사용하며 (그리고 사랑하며) 느낀 실망스러운 점들
(reddit.com)Rust를 사용한 지 대략 10년이 되었고, 이 언어를 정말 사랑합니다. 하지만 몇 가지 실망스러운 점들도 존재합니다. 아래는 그 목록입니다.
1. Result<T, E>의 문제
Rust의 오류 처리가 명확하고 강제적이라는 점은 훌륭합니다. 그러나 실제로 사용하다 보면 불편함이 많습니다.
- 라이브러리 작성자의 어려움: 새로운 오류 타입을 만들고 변환하는 과정이 번거롭습니다. 종속성을 추가할 때마다 각 함수의 오류 타입을 래퍼 오류 타입에 추가하는 작업은 특히 귀찮습니다.
- 애플리케이션 코드의 번거로움: 함수가 왜 실패했는지보다는 오류를 상위로 전파하고 사용자에게 결과를 보여주는 것이 중요합니다. Java와 달리 Rust는 전파 과정에서 백트레이스를 제공하지 않아 문제의 원인을 파악하기 어렵습니다.
2. 모듈 시스템의 유연성
Rust의 모듈 시스템은 너무 유연하여 오히려 불편할 때가 많습니다.
- 과도한 유연성: 타입을 재수출하거나 접근 수준을 세밀하게 조정할 수 있지만, 이는 실수로 원하지 않는 타입을 노출시키는 결과를 초래할 수 있습니다.
- 고아 규칙의 문제: 프로젝트를 여러 크레이트로 나누는 것이 권장되지만, 고아 규칙이 때때로 방해가 됩니다.
3. 컴파일 시간과 IDE 도구
Rust의 컴파일 시간과 IDE 도구의 에러 체크가 너무 느립니다.
- 긴 컴파일 시간: 대규모 프로젝트에서는 한 함수를 수정하면 전체 크레이트가 다시 컴파일되며, 이는 매우 비효율적입니다.
- 느린 IDE 응답 속도: Rust analyzer는 타이핑할 때마다 프로젝트를 다시 인덱싱하는 것처럼 느껴지며, 이는 대규모 프로젝트에서 특히 문제가 됩니다.
결론
Rust는 제가 가장 좋아하는 언어이지만, 이러한 실망스러운 점들도 존재합니다. 다른 사용자들도 같은 문제를 겪고 있는지 궁금합니다.
긴 컴파일 시간에 대해서는 아래의 댓글이 도움이 되실 것 같아 추가합니다: (by pr4wl)
Rust analyzer가 매번 변경할 때마다 긴 재컴파일을 수행한다면, 이는 아마도 애플리케이션을 빌드할 때 사용하는 기능이나 환경 변수가 다르기 때문일 것입니다. 기본적으로 RA는 빌드 아티팩트를 저장하기 위해 cargo build와 동일한 타겟 디렉토리를 사용하며, 서로 호환되지 않는 빌드를 수행하면 계속해서 전체 빌드를 수행하게 됩니다.
이 문제는 특히 Bevy에서 bevy/dynamic_linking 기능을 빌드에 사용하고 있지만 Rust analyzer에는 사용하지 않는 경우에 흔히 발생할 수 있습니다.
가장 간단한 해결 방법은 RA에 다른 타겟 디렉토리를 사용하도록 지시하는 것입니다. 이와 관련된 자세한 내용은 rust-analyzer.cargo.targetDir에서 확인할 수 있습니다.
또 다른 해결 방법은 모든 기능과 환경 변수가 동일하도록 설정하여 서로의 빌드 아티팩트를 재사용할 수 있도록 하는 것입니다. 하지만 이는 까다로울 수 있습니다.
라이브러리 작성자의 어려움: [..snip..] 종속성을 추가할 때마다 각 함수의 오류 타입을 래퍼 오류 타입에 추가하는 작업은 특히 귀찮습니다.
이 부분은 정말 뼈저리게 느껴집니다. 크레이트 전용 에러 enum
을 만들고 의존성에서 당겨오는 에러 타입을 위해 매번 impl From<ExtError> for Error
를 적으면서 '귀찮아 죽겠네'라고 생각한 적이 한두 번이 아니라서요...