Go vs Rust? Go를 선택하라.
원문은 Matthias Endler님이 에 게재했습니다. 이 블로그 구독하기
이 글은 오래전에 작성한 글입니다. 그 사이 일부 내용에 대한 생각이 바뀌었습니다.
장단점을 보다 균형 잡힌 시각으로 보고 싶다면, Shuttle과 협업해 새로 작성한 Go vs Rust 비교 글을 대신 읽어보시길 권합니다. 🚀
“Rust와 Go 중 뭘 선택해야 할까요?”라는 질문을 자주 받습니다. 두 언어 모두 같은 사용자층을 두고 경쟁하는 것처럼 보이고, 둘 다 시스템 프로그래밍 언어처럼 보이니 당연히 명확한 승자가 있어야 할 것 같죠?
Go: 실용적이고, 실무적이며, 단순하다

Go가 우아한 언어라고는 생각하지 않습니다. Go의 가장 큰 장점은 단순함 쉬움입니다. Go는 사실 시스템 프로그래밍 언어라고 하기도 어렵습니다. 백엔드 인프라를 위한 마이크로서비스나 툴을 만드는 데는 훌륭하지만, 커널이나 메모리 할당자를 Go로 작성하고 싶지는 않습니다.
하지만 Go로는 일을 빠르게 해낼 수 있습니다 — 빠르게.
Go는 제가 써본 언어 중 가장 생산성이 높은 언어 중 하나입니다. 핵심은 바로 이것입니다: 오늘 당장 현실의 문제를 해결하라.
Rust의 강력한 보장은 대가를 치러야 한다

반면 Rust는 어렵습니다. 어느 정도 생산적으로 다룰 수 있게 되기까지 수개월이 걸렸습니다. 이점을 체감하려면 상당한 시간을 투자해야 합니다. Rust는 이미 강력한 언어이며 날이 갈수록 더 강력해지고 있습니다. 제게는 더 안전한 C라기보다 실용적인 Haskell에 가깝게 느껴집니다.
오해는 마세요. 저는 Rust를 사랑합니다. Rust 덕분에 더 나은 프로그래머가 될 수 있었죠. 분명 배워볼 만한 멋진 언어입니다. 다만 다음 대형 프로젝트에 적합한 선택인지는 별개의 문제입니다.
핵심은 이겁니다. Rust를 선택한다면 보통 이 언어가 제공하는 보장이 필요하기 때문입니다.
Null포인터, 레이스 컨디션, 온갖 로우레벨 위협으로부터의 안전성.- 예측 가능한 런타임 동작(제로 코스트 추상화와 가비지 컬렉터 없음).
- 하드웨어에 대한 (거의) 완전한 제어(메모리 레이아웃, 프로세서 기능).
- 다른 언어와의 매끄러운 상호 운용성.
이런 기능 중 어느 하나도 꼭 필요하지 않다면, Rust는 다음 프로젝트에 그다지 좋은 선택이 아닐 수 있습니다. 이러한 보장에는 대가가 따르기 때문입니다. 바로 적응 기간입니다. 나쁜 습관을 버리고 새로운 개념을 배워야 합니다. 처음에는 borrow checker와 씨름할 가능성이 높습니다.
사례 연구: 시도 나눗셈으로 소수 판별하기
어떤 수가 소수인지 확인하고 싶다고 해보겠습니다. 가장 쉬운 방법은 그 수보다 작은 자연수 중 나누어떨어지는 수가 있는지 확인하는 것입니다(나머지 없이). 하나도 없다면 그 수는 소수입니다! 이 방식을 시도 나눗셈(trial division)이라고 합니다.
Golang으로는 이렇게 할 수 있습니다(Rosetta Code 제공):
func IsPrime(n int) bool {
if n < 0 {
n = -n
}
switch {
case n < 2:
return false
default:
for i := 2; i < n; i++ {
if n%i == 0 {
return false
}
}
}
return true
}같은 작업을 Rust로는 이렇게 합니다:
pub fn is_prime(n: u64) -> bool {
match n {
0...1 => false,
_ => {
for d in 2..n {
if n % d == 0 {
return false;
}
}
true
}
}
}언뜻 보면 두 해법은 꽤 비슷해 보입니다. 하지만 자세히 보면 몇 가지 차이점을 발견할 수 있습니다.
- Go에서는 단순한 switch-case 문을 사용합니다. Rust에서는 훨씬 더 강력한
match문을 사용합니다. - Go에서는 2부터 n까지 숫자를 순회하기 위해 단순한 for 루프를 사용합니다. Rust에서는 범위 표현식(
2..n)을 사용합니다. - Go에서는 두 개의
return문을 사용하지만, Rust에서는 하나의return표현식을 사용합니다. 일반적으로 Rust에서는 대부분의 것이 표현식이며, 반환하거나 변수에 할당할 수 있습니다. 표현식에 대해 더 자세히 알고 싶다면 여기를 참고하세요.
여러 면에서 Rust는 Golang보다 더 함수형에 가깝습니다. 위 코드를 Range에 구현된 any 메서드를 사용해 다시 작성할 수도 있습니다.
fn is_prime(n: u64) -> bool {
match n {
0...1 => false,
_ => !(2..n).any(|d| n % d == 0),
}
}처음에는 다소 낯설게 느껴질 수 있지만, 시간이 지나면 자연스러워질 겁니다.
물론 이는 간단한 예시에 불과합니다. 두 언어에 대한 감을 더 잡고 싶다면 Rosetta Code에서 코드를 둘러보시길 권합니다.
사례 연구: 텍스트 파일에서 중복 단어 찾기
시각적인 설명을 선호한다면, 두 언어를 비교하기 위해 Go와 Rust로 간단한 동시성 프로그램을 작성하는 영상도 준비해 두었습니다:
Go에서 더 마음에 드는 점
- 빠른 컴파일 속도
- 실용적인 문제 해결 방식
- 전형적인 DevOps 작업을 위한 훌륭한 생태계
- 풍부한 표준 라이브러리
- IDE 지원
- 단순한 에러 처리
- 마스코트 😉
Rust에서 더 마음에 드는 점
- 안전성: null 포인터 없음, 데이터 레이스 없음,…
- 세밀한 시스템 제어
- 놀라운 런타임 속도(C/C++에 버금감)
- 제로 코스트 추상화
- 멋지고 개방적인 커뮤니티
- cargo를 통한 간편한 패키지 관리
- trait 형태의 제네릭 지원
- C 상호 운용성과 FFI
결론
99%의 경우 Go면 “충분히” 좋고, 나머지 1%에서 충분하지 않을 때는 여러분도 알게 될 겁니다. 그때 Rust를 살펴보세요. 두 언어는 서로를 꽤 잘 보완합니다. 실전 Rust 컨설팅에 관심이 있다면 캘린더에서 날짜를 선택해 어떻게 도와드릴 수 있을지 이야기 나눠 봅시다.
글을 무작위로 읽기
댓글
로그인하고 댓글 남기기