콘텐츠로 이동

Rust vs C++, Go, Python

“Rust를 배울까, Go를 배울까, 아니면 그냥 Python을 계속 쓸까?” Rust를 처음 알게 된 사람들이 가장 많이 하는 질문 중 하나예요. 솔직한 답은 “무엇을 만들고 싶은지에 따라 다르다”예요. 이 페이지에 나오는 언어는 모두 좋은 언어이고, 수백만 명이 쓰고 있어요. 다만 각자 다른 부분을 포기하고 다른 부분을 얻었을 뿐이에요.

언어를 탈것에 비유해 볼게요. 스포츠카, 택배 트럭, 자전거는 모두 “좋은” 탈것이에요. 그래도 자전거로 이삿짐을 옮기지는 않잖아요. 이 페이지에서는 각 언어의 장단점을 쉬운 말로 설명해서, 여러분의 일에 맞는 도구를 고를 수 있게 도와드릴게요. Rust의 약점까지 포함해서 최대한 공정하게 다루려고 해요.

Rust의 가장 큰 약속은 속도와 안전성을 동시에 준다는 거예요.

  • C, C++만큼 빨라요. Rust는 네이티브 기계어로 컴파일되고 가비지 컬렉터가 없어서, 메모리를 정리하느라 프로그램이 멈추는 일이 없어요.
  • 가비지 컬렉터 없이도 메모리가 안전해요. 소유권과 대여 규칙은 컴파일할 때 검사돼요. 해제된 메모리 사용이나 스레드 간 데이터 경쟁 같은 버그 묶음이 프로그램이 실행되기도 전에 걸러져요.
  • 처음부터 훌륭한 도구가 갖춰져 있어요. Cargo라는 도구 하나로 코드를 빌드하고, 라이브러리를 내려받고, 테스트를 돌리고, 문서를 만들 수 있어요. 포매터(rustfmt)와 린터(clippy)도 공식 도구예요.
  • 컴파일러 에러 메시지가 친절해요. 문제를 정확히 어떻게 고치면 되는지 알려 주는 경우가 많아요.

개발자들도 Rust를 좋아하는 것 같아요. 2025년 Stack Overflow 개발자 설문(영어)에서 Rust는 또다시 “가장 높이 평가받는(most admired)” 언어로 뽑혔어요. 써 본 사람 대부분이 계속 쓰고 싶어 한다는 뜻이에요.

간단한 것부터 시작해 볼게요. 목록에 있는 짝수를 모두 더하는 프로그램이에요. 먼저 Rust로 쓰면 이래요.

fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
let sum: i32 = numbers.iter().filter(|n| *n % 2 == 0).sum();
println!("짝수의 합: {sum}");
}

실행 결과:

짝수의 합: 12
▶ 직접 실행해 보기

같은 프로그램을 C++로 쓰면:

#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6};
int sum = 0;
for (int n : numbers) {
if (n % 2 == 0) {
sum += n;
}
}
std::cout << "짝수의 합: " << sum << "\n";
}

Go로 쓰면:

package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5, 6}
sum := 0
for _, n := range numbers {
if n%2 == 0 {
sum += n
}
}
fmt.Println("짝수의 합:", sum)
}

그리고 Python으로 쓰면:

numbers = [1, 2, 3, 4, 5, 6]
total = sum(n for n in numbers if n % 2 == 0)
print("짝수의 합:", total)

네 프로그램 모두 같은 줄을 출력해요. Python이 가장 짧고, Go와 C++는 평범한 반복문을 쓰고, Rust는 클로저를 넘긴 반복자 체인을 써요(Rust에서도 for 반복문으로 쓸 수 있어요). 이 정도 작은 프로그램에서는 차이가 대부분 스타일 문제예요. 진짜 차이는 더 큰 프로그램에서 드러나요.

C++와 Rust는 같은 영역을 노려요. 브라우저, 게임 엔진, 데이터베이스, 운영체제처럼 메모리를 완전히 제어하면서 빠르게 돌아가야 하는 프로그램이죠. 가장 큰 차이는 메모리를 올바르게 쓰는지 누가 검사하느냐예요.

C++에서는 여러분이 직접 검사해야 해요. 대표적인 버그를 하나 볼게요. C++ 컴파일러는 이 코드를 아무 불평 없이 받아들여요. 하지만 push_back이 벡터를 새 메모리로 옮길 수 있어서, first가 이미 해제된 메모리를 가리키게 될 수 있어요.

std::vector<int> v = {1, 2, 3};
int& first = v[0];
v.push_back(4); // 벡터의 메모리가 옮겨질 수 있어서...
std::cout << first << "\n"; // ...여기서 해제된 메모리를 읽을 수 있어요

똑같은 실수를 Rust에서 하면 컴파일이 되지 않아요.

fn main() {
let mut v = vec![1, 2, 3];
let first = &v[0];
v.push(4);
println!("{first}");
}
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
--> src/main.rs:4:5
|
3 | let first = &v[0];
| - immutable borrow occurs here
4 | v.push(4);
| ^^^^^^^^^ mutable borrow occurs here
5 | println!("{first}");
| ----- immutable borrow later used here

에러 메시지는 “v가 이미 불변으로 대여된 상태라서 가변으로 대여할 수 없다”는 뜻이에요. 3번 줄에서 불변 대여가 일어났고, 4번 줄에서 가변 대여를 하려 했고, 5번 줄에서 불변 대여를 아직 쓰고 있다고 짚어 주고 있어요.

이건 실제로 아주 중요해요. Chromium 팀은 심각한 보안 버그의 약 70%가 메모리 안전성 문제(영어)라고 밝혔어요. Google의 Android 팀은 몇 년째 새 코드를 Rust로 작성해 왔어요. 2022년에는 Android의 Rust 코드에서 메모리 안전성 취약점이 하나도 발견되지 않았다(영어)고 발표했고, 2025년에는 C, C++ 코드보다 메모리 안전성 취약점 밀도가 1000배 넘게 낮다(영어)고 발표했어요. 리눅스 커널(영어)도 Rust로 작성한 코드를 지원해요.

도구: C++에는 빌드 시스템과 패키지 관리자가 여러 개 있어서, 팀이 설정에 상당한 시간을 쓰는 경우가 많아요. Rust에는 표준 도구인 Cargo가 하나 있어서, 모든 Rust 프로젝트가 같은 방식으로 빌드돼요.

학습 난이도: 둘 다 어려워요. C++는 거대한 언어이고, 많은 규칙을 실행 중에 실수하면서 배우게 돼요. Rust의 규칙은 엄격하지만 컴파일러가 강제하고 설명까지 해 주기 때문에, 컴파일 시점에 배우게 돼요.

여전히 C++가 유리한 점: 수십 년간 쌓인 기존 코드, 라이브러리, 숙련된 개발자예요. 회사나 게임 엔진이 C++ 기반이라면 현실적으로 C++가 맞는 선택인 경우가 많아요. Rust는 C 코드를 호출할 수 있고, C++도 C 스타일 인터페이스를 통해 호출할 수 있지만, 둘을 섞으려면 품이 들어요.

Go와 Rust는 자주 비교돼요. 둘 다 현대적인 컴파일 언어이고, 서버와 명령줄 도구에 많이 쓰이니까요. 하지만 설계 목표가 달라요.

가비지 컬렉션 vs 소유권. Go는 프로그램이 실행되는 동안 쓰지 않는 메모리를 찾아서 해제하는 가비지 컬렉터(영어)를 써요. 그래서 Go 코드는 작성하기 더 쉬워요. 값의 주인이 누구인지 신경 쓸 필요가 없거든요. 대신 메모리와 CPU를 조금 더 쓰고, 짧은 멈춤이 생겨요. Rust에는 가비지 컬렉터가 없어요. 메모리는 주인이 스코프를 벗어나는 정해진 시점에 해제돼요. 그래서 성능을 더 잘 예측할 수 있지만, 소유권과 대여를 배워야 해요.

동시성. Go의 고루틴(goroutine)과 채널은 쉽기로 유명해요. 숫자 세 개를 병렬로 제곱해서 더하는 작은 예제를 볼게요.

results := make(chan int)
for n := 1; n <= 3; n++ {
go func() {
results <- n * n
}()
}
total := 0
for i := 0; i < 3; i++ {
total += <-results
}
fmt.Println("합계:", total)

Rust 버전은 스레드와 채널을 써요. 조금 더 길어요. 스레드마다 송신자(sender)를 복제하고 move를 써야 하거든요.

use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
for n in 1..=3 {
let tx = tx.clone();
thread::spawn(move || {
tx.send(n * n).unwrap();
});
}
drop(tx); // 아래 반복문이 끝날 수 있도록 우리 쪽 송신자를 닫아요
let total: i32 = rx.iter().sum();
println!("합계: {total}");
}

실행 결과:

합계: 14
▶ 직접 실행해 보기

이렇게 조금 더 타이핑해서 얻는 게 있어요. Rust는 두 스레드가 같은 데이터를 동시에 바꿀 수 있는 코드(데이터 경쟁)를 아예 컴파일하지 않아요. Go에서는 데이터 경쟁이 생길 수 있어요. Go는 프로그램 실행 중에 데이터 경쟁을 찾아 주는 경쟁 탐지기(race detector)(영어)를 제공해요. 자세한 내용은 동시성을 보세요.

컴파일 시간. Go는 빠르게 컴파일되도록 만들어졌어요. Go FAQ에는 큰 실행 파일도 길어야 몇 초 안에 빌드되어야 한다(영어)고 적혀 있어요. Rust는 컴파일러가 훨씬 많은 검사와 최적화를 하기 때문에 더 느리게 컴파일돼요. 큰 프로젝트에서는 체감이 될 거예요.

단순함. Go는 일부러 작게 만든 언어라서 배울 기능이 적고, 많은 사람이 Go 코드를 빨리 읽을 수 있다고 느껴요. Rust에는 트레이트, 제네릭, 라이프타임, 매크로처럼 기능이 더 많아요. 그만큼 강력하지만 배우는 데 시간이 더 걸려요.

Python과 Rust는 아주 달라요. 그래서 하나를 다른 하나 대신 쓰기보다는 함께 쓰는 경우가 많아요.

속도. Python은 인터프리터 언어라서, 계산을 많이 하는 순수 Python 코드는 컴파일된 코드보다 훨씬 느려요. 그래서 인기 있는 Python 라이브러리들은 무거운 작업을 내부적으로 C, C++, Rust로 처리하는 경우가 많아요.

타입. Python에서는 프로그램이 실행되는 동안 타입을 검사해요. 아래 함수는 숫자를 넣으면 잘 동작하고 문자열을 넣으면 실패하는데, 그 사실은 해당 줄이 실행될 때에야 알 수 있어요.

def add_tax(price):
return price * 1.2
print(add_tax(10)) # 12.0
print(add_tax("10")) # TypeError: can't multiply sequence by non-int of type 'float'

Python에도 선택적인 타입 힌트가 있지만, Python 런타임은 타입 힌트를 강제하지 않아요. Rust에서는 프로그램이 실행되기 전에 모든 타입을 검사해요.

fn add_tax(price: f64) -> f64 {
price * 1.2
}
fn main() {
println!("{}", add_tax(10.0));
println!("{}", add_tax("10"));
}
error[E0308]: mismatched types
--> src/main.rs:7:28
|
7 | println!("{}", add_tax("10"));
| ------- ^^^^ expected `f64`, found `&str`
| |
| arguments to this function are incorrect

“타입이 맞지 않는다”는 에러예요. 함수는 f64를 기대했는데 &str(문자열)을 받았다고 알려 주고 있어요.

여전히 Python이 더 나은 경우: 간단한 스크립트, 데이터 분석, 머신러닝, 노트북, 자동화, 그리고 초보자 교육이에요. Python의 데이터 과학 생태계는 어마어마하고, 아이디어를 몇 분 만에 시험해 볼 수 있어요. 프로그램이 대부분의 시간을 네트워크나 데이터베이스를 기다리는 데 쓴다면, Python의 속도는 크게 문제가 되지 않는 경우가 많아요.

둘 다 쓰기: PyO3(영어) 프로젝트를 쓰면 Python 모듈을 Rust로 작성할 수 있어요. 그래서 Python 코드는 그대로 두고 느린 부분만 Rust로 옮길 수 있어요. 인기 있는 Python 도구 중에는 Rust로 만든 것도 있어요. 예를 들어 Stack Overflow 설문에서도 Python 패키지 관리자 uv가 Rust로 만들어졌다(영어)고 언급해요.

엄격한 규칙이 아니라 일반적인 경향이에요. 어떤 언어든 잘 작성한 프로그램이라면 “충분히 빠를” 수 있어요.

Rust C++ Go Python
속도 매우 빠름, 네이티브 코드, GC 없음 매우 빠름, 네이티브 코드, GC 없음 빠름, GC가 있는 네이티브 코드 순수 Python으로 무거운 계산을 하면 느린 편
메모리 안전성 컴파일 시점에 검사 (unsafe 밖에서) 프로그래머 책임 GC, 데이터 경쟁은 여전히 가능 GC, 안전하지만 타입 에러는 실행 중에 발생
학습 난이도 처음엔 가파름 가파르고, 언어 자체가 매우 큼 완만함 가장 완만함
컴파일 시간 느린 편 느린 편 (프로젝트에 따라 다름) 매우 빠름 컴파일 단계 없음
생태계 비교적 젊고 성장 중, Cargo 내장 거대하고 성숙함, 빌드 도구가 여러 개 표준 라이브러리가 탄탄해서 서버에 강함 어마어마함, 특히 데이터와 ML
주요 용도 CLI 도구, 서버, 시스템 코드, WebAssembly, 임베디드 게임, 브라우저, 고성능 시스템, 레거시 시스템 웹 서비스, 클라우드와 DevOps 도구 스크립트, 데이터 과학, ML, 자동화

스스로에게 이런 질문을 해 보세요.

  • 최고 속도나 적은 메모리 사용이 필요한가요? 게임 엔진, 데이터베이스, 작은 기기에서 돌아가는 코드 같은 경우요. Rust(또는 C++)가 좋은 선택이에요.
  • 버그가 생기면 큰 비용이 드나요? 보안에 민감한 코드, 오래 돌아가는 서버, 많은 사람이 의존하는 코드는 Rust의 컴파일 시점 검사에서 가장 큰 이득을 봐요.
  • 다른 사람과 나눠 쓸 명령줄 도구를 만드나요? Rust는 별도 런타임 설치가 필요 없는 빠른 실행 파일 하나를 만들어 줘요. 이 분야에서는 Go도 훌륭해요.
  • 이 언어들이 모두 처음인 팀과 함께 웹 서비스를 빨리 만들어야 하나요? 아마 Go가 더 빨리 목표에 도달할 거예요. Rust도 잘 맞지만(프로젝트: Axum으로 웹 API 참고), 배우는 시간이 더 길어요.
  • 간단한 스크립트, 데이터 분석, ML 실험인가요? Python을 쓰세요. 나중에 어떤 부분이 너무 느려지면 그때 PyO3로 Rust를 도입하면 돼요.
  • 거대한 기존 C++ 코드베이스가 있나요? 당분간은 C++를 유지하거나, Android처럼 새로운 별도 부분에만 Rust를 도입해 보세요.

그리고 그냥 더 나은 프로그래머가 되고 싶다면, Rust를 배우면 메모리가 실제로 어떻게 동작하는지 알게 돼요. 그 지식은 다른 모든 언어에서도 도움이 돼요.

Rust도 완벽하지 않아요. 사람들이 가장 힘들어하는 부분은 이런 것들이에요.

  • 학습 곡선은 진짜예요. 대여 검사기(borrow checker)와 함께하는 처음 몇 주는 답답할 수 있어요. 한동안은 “컴파일러와 싸운다”는 느낌을 각오하세요. 소유권에 감이 오면 훨씬 쉬워져요.
  • 컴파일 시간이 Go보다 느려요. 특히 큰 프로젝트와 릴리스 빌드에서요.
  • 표현하기 더 어려운 것들이 있어요. 객체끼리 연결이 많은 그래프 형태의 데이터나, 가변 상태를 많이 공유하는 코드는 스마트 포인터 같은 도구가 필요하고, 미리 더 많이 설계해야 해요.
  • 생태계가 비교적 젊어요. GUI 앱이나 일부 과학 계산 라이브러리 같은 분야에서는 C++나 Python보다 성숙한 선택지가 적어요.
  • (아직은) 쓰는 사람이 적어요. 2025년 Stack Overflow 설문(영어)에서 Python(약 58%)이나 C++(약 24%)를 쓴다고 답한 사람이 Rust(약 15%)보다 훨씬 많았어요. 그만큼 회사에서 채용하거나, 물어보거나, 배울 수 있는 숙련된 Rust 개발자가 적다는 뜻이에요.
  • 간단한 일에도 코드가 더 많이 필요해요. 채널 예제에서 봤듯이, 다른 언어가 느슨하게 넘어가는 부분을 Rust는 명시적으로 쓰라고 요구할 때가 많아요.
  • Rust는 C++ 수준의 속도에 더해 컴파일 시점에 검사되는 메모리 안전성을 제공하고, 기본 도구도 훌륭해요.
  • C++와 비교하면, Rust는 여러 종류의 메모리 버그를 프로그램이 실행되기 전에 막아요. C++에는 훨씬 많은 기존 코드와 라이브러리가 있어요.
  • Go와 비교하면, Rust는 가비지 컬렉터가 없고 데이터 경쟁을 컴파일 시점에 막아요. Go는 더 단순하고 훨씬 빨리 컴파일돼요.
  • Python과 비교하면, Rust는 훨씬 빠르고 실행 전에 타입을 검사해요. Python은 더 빨리 작성할 수 있고 데이터 과학 분야를 꽉 잡고 있어요. PyO3로 둘을 함께 쓸 수 있어요.
  • Rust의 주요 비용은 가파른 학습 곡선, 느린 컴파일, 비교적 젊은 생태계예요.

이해도 점검

1. Rust는 가비지 컬렉터 없이 어떻게 메모리를 관리할까요?

2. 다음 중 Go가 Rust보다 나은 점으로 공정한 것은 무엇일까요?

3. Python 프로그램에서 함수 하나가 너무 느려요. 흔히 쓰는 방법은 무엇일까요?

4. C++ vs Rust 예제에서, &v[0]을 붙잡아 둔 채로 v에 값을 push하는 코드는 어떻게 될까요?

점수: 0 / 4