Rust(프로그래밍 언어)

덤프버전 :

파일:다른 뜻 아이콘.svg
은(는) 여기로 연결됩니다.
게임에 대한 내용은 러스트(게임) 문서
러스트(게임)번 문단을
러스트(게임)# 부분을
, {{{#!html }}}에 대한 내용은 문서
#s-번 문단을
#s-번 문단을
# 부분을
# 부분을
, {{{#!html }}}에 대한 내용은 문서
#s-번 문단을
#s-번 문단을
# 부분을
# 부분을
, {{{#!html }}}에 대한 내용은 문서
#s-번 문단을
#s-번 문단을
# 부분을
# 부분을
, {{{#!html }}}에 대한 내용은 문서
#s-번 문단을
#s-번 문단을
# 부분을
# 부분을
, {{{#!html }}}에 대한 내용은 문서
#s-번 문단을
#s-번 문단을
# 부분을
# 부분을
, {{{#!html }}}에 대한 내용은 문서
#s-번 문단을
#s-번 문단을
# 부분을
# 부분을
, {{{#!html }}}에 대한 내용은 문서
#s-번 문단을
#s-번 문단을
# 부분을
# 부분을
, {{{#!html }}}에 대한 내용은 문서
#s-번 문단을
#s-번 문단을
# 부분을
# 부분을
, {{{#!html }}}에 대한 내용은 문서
#s-번 문단을
#s-번 문단을
# 부분을
# 부분을
참고하십시오.





프로그래밍 사이트 선정 프로그래밍 언어 순위 목록

⠀[ IEEE Spectrum 2021 ]⠀
{{{#!wiki style="display: inline-block; margin: 0 0 -5px; min-width: 25%"
⠀[ Stack Overflow 2022 ]⠀
{{{#!wiki style="display: inline-block; margin: 0 0 -5px; min-width: 25%">
⠀[ TIOBE 2023 ]⠀

프로그래밍 언어 목록 · 분류 · 문법


1. 개요
2. 역사
3. 언어의 특징
3.1. 안전한 메모리 관리
3.1.1. 소유권과 수명
3.1.2. 가변성
3.2. 철저한 에러 관리
3.3. 편리한 열거형
3.4. 트레이트
3.4.1. 특수한 트레이트
3.5. 하이지닉 매크로
3.6. 비동기 프로그래밍
3.7. 제네릭
4. 개발 도구별 특징
4.1. 안전하고 강력한 컴파일러
4.2. 편리한 패키지 관리 도구
4.3. 텍스트 에디터 지원
5. 주목받고 있는 언어
5.1. 개발자들이 가장 좋아하는 언어
5.2. 대기업에 주목받고 있는 언어
5.3. 운영체제 개발 언어
6. 사용 현황 및 전망
6.1. 전망
6.2. 기존 언어와의 비교
6.2.1. Rust vs C++
6.2.1.1. 결론
6.2.2. Rust vs Carbon
6.3. 사용 현황
6.3.1. Rust를 사용하는 기업 및 단체
6.3.2. Rust로 제작된 것들
8. 도서
8.1. The book
8.2. 그 외
9. 기타
10. 관련 링크


1. 개요[편집]


fn main() {
    println!("hello world");
}

러스트 재단에서 개발되고 있는 메모리 안전성성능 및 편의성에 중점을 둔 프로그래밍 언어. 가비지 컬렉터 없이 메모리 안전성을 제공하는 대표적인 언어다. C++의 대체재로써 등장했다.

모질라 재단에서 2010년 7월 7일에 처음 발표했으며, 2015년 5월 15일에 안정 버전이 정식 발표된 이후, 2021년 2월부터는 러스트 재단으로 분리되어 AWS, Google, 화웨이, MS, 모질라 재단을 초기 회원사로 발족했다.

이 언어를 대표하는 키워드 몇 개를 나열해보면 안전성, 속도, 병렬 프로그래밍, 함수형 프로그래밍, 시스템 프로그래밍이 있다. Go보다는 반 년 늦게 나왔지만[1] 그나마 비슷한 시기에 등장했다는 점과 두 언어 모두 C/C++를 서로 다른 방향에서 대체하려 한다는 점 때문에 라이벌 관계로 엮이기도 한다.

온라인상으로 표준 라이브러리 기반의 코드를 실행해볼 수 있다. #

Rust의 비공식 마스코트[2]도 있는데, 이름은 페리스(Ferris)다. 밝은 주황색의 게 모양을 하고 있으며, 러스트 관련 커뮤니티나 미디어에서 자주 등장한다.[3] 또한 이 페리스 때문에 Rust 개발자는 스스로를 Rustacean[4][5]이라고 자칭한다.


2. 역사[편집]


자세한 내용은 Github의 rust-lang/rust 릴리스 참조.

2018년 12월 6일에 발표된 1.31.0 버전을 기점으로 Rust 2018 Edition으로 에디션이 변경되고 (가이드 북) 1.31.0 이전 버전은 Rust 2015 Edition으로 정의됐다.
1.56.0 이후 버전은 Rust 2021 Edition이 적용됐다.

원래는 모질라 소속의 개발자인 그레이던 호어의 개인 프로젝트였으나, 모질라 재단의 차기 웹 브라우저 엔진 프로젝트인 서보(Servo)를 개발하는 데에 쓰기 위해 함께 연구 프로젝트로 편입됐다.[6] 자세한 내용은 서보 참고.


3. 언어의 특징[편집]


현대적인 시스템 프로그래밍 언어로 C/C++와 동등한 수준의 속도를 유지하면서 안전성과 동시성을 향상시키는 것을 목표로 설계되었다. 그래서 '안전한 코드'에서는 포인터 역참조와 같이 메모리 관리 실수를 범하기 쉬운 일부 기능들의 사용을 제한한다. 모든 변수는 RAII[7]가 강제되며, 컴파일러는 모든 변수의 수명과 참조자의 유효성을 검증한다.

함수형 프로그래밍 언어로부터 발전된 타입 시스템을 도입했으며, 객체 상속 대신 다른 언어에서의 인터페이스와 비슷한 트레이트라는 개념을 기반으로 다형성을 달성한다.[8] 타입이 강제되는 매크로를 사용해 언어를 확장하는 것이 가능하며, 현대적인 모듈 시스템을 통해 쉽게 모듈화될 수 있다.

null 포인터 에러가 언어 차원에서 존재하지 않는다. 필요한 경우에는
Option<T>
이라는 제네릭 열거형에 감싸서 사용한다. 그리고
match
if let
를 사용하는 패턴 매칭을 통해 가능한 경우들에 대해 개발자가 적절한 제어를 하도록 유도한다. 이렇게 존재가 애매모호한 상태를 언어 설계 차원에서 피하여 개발자가 실수할 가능성을 상당히 줄였다. 타입의 모호함도 방지하기 위함인지
++
--
와 같은 전/후위연산자가 존재하지 않는다.

3.1. 안전한 메모리 관리[편집]


Rust는 메모리 관리의 안전성이 상당히 고려된 언어이다.[9] 무분별한 참조를 막는 제약이 있고, 오버헤드가 없는 안전한 메모리 관리를 위해 후술할 Ownership(소유권)과 Lifetime(수명)이라는 다른 언어에는 없는 개념을 이용해 컴파일 단계에서 메모리 관리를 한다. 여기에 더해, 컴파일 설정을 Debug(개발단계에 사용)로 해두면 Integer overflow같은 오류들도 런타임에 잡아낼 수 있고, overflow가 예상되는 지점에만 표준 라이브러리가 제공하는 기능들을 적용하면 release 버전에서도 overflow에 대한 예외처리를 할 수 있다.

Rust는 시스템 프로그래밍 언어이니만큼, Zero-cost abstraction(무비용 추상화)를 지향하기 때문에 쓰레기 수집을 사용하지 않는다[10]. 그리고 기본적으로 C/C++와 마찬가지로 시스템에서 메모리를 직접 할당받아 사용한다.[11][12] 포인터에서 값을 직접 가져오거나 함수를 호출하는 것은 금지된다.
unsafe
스코프나 함수 내에서는 허용되지만, 이는 안전하지 않은 코드를 안전한 코드로 추상화하거나, 하드웨어를 직접 다루기 위해 존재하는 것이다. 최적화를 통한 퍼포먼스 향상에도 사용될 수 있지만 컴파일러보다 잘 할 자신이 없으면 이 의도로는 건들지 않는 게 현명하다


3.1.1. 소유권과 수명[편집]


안전성과 성능 모두를 위해 고려된 개념으로 기존 프로그래밍 언어에는 없던 생소한 개념이다. 처음부터 본 문단을 완전히 이해할 필요는 없으며 직접 코드를 작성해보면서 모르는 점이 있을 때 참고하면 된다.

소유권(ownership)은 객체의 생성과 소멸을 관리하기 위한 개념이며, 수명 파라메터(lifetime parameter)은 컴파일러가 참조자의 유효성을 검증할 수 있도록 하기 위한 개념이다. 스코프(scope)는 코드를 둘러싸는 중괄호를 말한다.

모든 값[13]은 그 값이 대입된 변수나 구조체 필드, 넘겨받은 함수 인자 등의 이름에 귀속된다. 이름은 자기에게 귀속된 값에 대한 소유권을 가지며, 다른 변수에게 값을 대입하면 그 이름으로 소유권이 이전되고 기존 변수는 파기된다. 그러므로 기존 변수에 대한 소멸자는 호출되지 않는다.[14] 힙에 할당된 값은 스택에 할당되는 객체에 같이 묶여 관리된다.

a = b
와 같은 연산은 기본적으로 복사 연산이 아닌 이동 연산이다.[15] 이동 연산은 값의 소유권을 새로운 변수로 이전하며 기존 변수는 파기한다. 다만, 단순히 메모리 값을 복사만 해도 되는 타입에는
Copy
트레이트를 객체에 부여하여 복사 연산을 수행하도록 할 수 있으며 이 경우 기존 변수가 파기되지 않는다.
std::vec::Vec
과 같이 힙에 할당된 포인터가 있어서 값을 단순히 복사하는 것으로 객체를 복제할 수 없는 경우에는 이동 연산을 사용하지만, 모든 정수, 실수, bool과 같은 primitive 타입은 Copy 트레이트를 부여하기 때문에 프로그래밍 할때 거슬리는 수준은 아니다.

값이 할당이 해제되지 않으면서 자신의 스코프 밖으로 나갈 수 있는 방법은 두 가지가 있다.
return
을 통해 소유권을 외부로 넘기거나[16], 외부의 객체가 내부에서 만들어진 객체의 소유권을 가져가면 된다. 값을 스코프 안쪽으로 집어넣기만 할 수도 있는데, 함수 인자를 통해 소유권을 함수로 넘기면 함수가 끝나는 시점에 값의 할당 해제를 발생시킬 수도 있다.

함수에 넘겨진 인자는 함수가 기본적으로 소유권을 가지게 된다. 함수가 반환하는 변수는 함수 바깥 스코프(Scope)로 소유권을 넘겨준다.[17] 만약 소유권을 넘겨주지 못한 채로 함수나 해당 스코프가 종료되면, 거기에 묶여 있던 변수도 수명이 끝나게 된다.

소유권 규칙에 따라 하나의 값은 언제나 하나의 이름으로만 접근할 수 있게 되는데, 실제로 이렇게만 프로그래밍을 하려면 제약이 너무 심하다. 따라서 Rust에서 다른 변수를 참조할 수 있도록 Borrowed pointer(`&`)[18]를 제공하고 있다. Borrowed pointer는 C나 C++에서의 포인터처럼 다른 변수를 참조할 수 있는데, 참조되는 변수는 참조하는 변수보다 수명이 같거나 길어야 한다. 쉽게 말하자면, "도서관에서 책을 빌렸으면 적어도 도서관이 망하기 전에는 책을 반납하시오"와 같다.[19] 책을 반납하러 도서관에 왔더니 이런 일이 생기면 안 된다는 소리이다. 멀티 스레드 참조와 같은 상황으로 인해, 수명을 컴파일 타임에 결정 할 수 없는 경우 컴파일 에러가 발생하게 된다.

수명 파라메터는 참조자의 수명을 검증하기 위한 일종의 제네릭이다. 참조자를 인자로 받는 함수나 참조자를 가지는 구조체 및 튜플에 정의하게 되며, (아직은 모를) 피참조 객체의 수명에 묶이게 된다. 만일 이러한 타입의 객체가 피참조 객체의 수명보다 길면 컴파일러가 에러를 표시한다. 각각의 수명 파라메터는 독립 관계나 종속 관계로 지정할 수 있다.
'b: 'a
로 표시하면
'b
'a
보다 수명이 길 수 없게 된다. 말만 들어서는 이러한 것이 왜 필요한지 이해하기 어려우므로 예제를 찾아보거나 ttf_parser와 같은 crate의 소스 코드를 읽어보는 것이 도움이 된다.

Rust는 이렇게 소유권과 수명을 컴파일 타임에 추적할 수 있도록 설계됐으며, 스택 영역에 할당되는 객체와 변수의 생성과 소멸 시기를 컴파일 타임에 모두 결정하고 검증하며 dangling 포인터를 방지한다. 소유권 개념을 스마트 포인터와 혼동하는 경우가 있는데, 스마트 포인터는 힙 영역에 할당되는 객체를 자동적으로 관리하기 위한 것으로 객체의 수명이 런타임에 결정된다.

힙 영역에 할당하는 객체의 경우, Rust 표준 라이브러리에서 제공되는
std::boxed::Box
std::rc::Rc
를 통해 스마트 포인터를 사용할 수 있다.
std::rc::Rc
와 같은 Reference Counting(참조 횟수 계산) 스마트 포인터의 경우, 순환 참조에 의한 메모리 누수가 여전히 발생할 수 있다는 점에 유의하자. 이 경우 참조 횟수를 추가하지 않는
std::rc::Weak
를 상황에 맞게 사용해서 해결 할 수 있다.

3.1.2. 가변성[편집]


Rust 언어에서는 모든 변수의 가변성을 명확하게 컴파일 타임에 구분한다. 변수 수정에 관한 규칙은 다음과 같다.
  • 한번 초기화된 변수는 기본적으로 읽기만 가능하고 변경이 불가능하다.
    • 특정 cell[20]로 정의된 변수나 그에 대한 참조자는 가변성 없이도 내부 데이터를 바꿀 수 있다.
  • mut
    키워드를 통해 변경 가능한 변수를 선언할 수 있다.
    • 참조의 경우, 변경 불가능한 변수를 변경 가능한 변수로 참조할 수 없다.
    • 가변 참조자는 스코프 내에서 두 개 이상 선언될 수 없다.

초보자 입장에서 상수와 가변성을 갖지 않는 변수를 헷갈려하는 경우가 있다. 상수는 컴파일 타임에 결정되는 값이며, 가변성을 갖지 않는 변수는 기본적으로 변경을 허용하지 않을 뿐이다.
RefCell
이나
UnsafeCell
를 사용하는 변수나 그에 대한 참조자는 가변성 없이도 내부 데이터를 변경할 수 있다.

가변성은 동시성을 제어하기 위해 설계된 것은 아니다. 멀티 스레드에서 변경 가능한 변수를 락이나 뮤텍스 없이 공유하는 것은 대부분의 경우에서 바람직한 방법이 아니다. 멀티 스레드 환경에서는, 변경 가능한 변수를
std::sync::Arc
std::sync::Mutex
와 같이 동시성 제어를 제공하는 타입으로 묶어서 안전하게 관리할 수 있다.

3.2. 철저한 에러 관리[편집]


Rust에는 예외가 없다. 대신 에러를 함께 제공하고자 하는 타입 `T`를
Result<T, E>
[21]로 묶어서 에러를 처리할 수 있도록 한다.

에러 처리를 철저히 관리해 프로그램의 안정성을 보장한다. 에러를 처리해주어야 하는 경우 함수에서
Result<T, E>
를 반환하는데, 에러를 처리할 것인지 패닉을 발생시켜 프로그램을 종료시킬 것인지 명시적으로 작성하도록 한다.

다른 언어의 예외와 비교하면 오버헤드가 거의 없기 때문에 Rust의 모토인 무비용 추상화(zero-cost abstraction)에 부합한다. C++의 경우 예외를 처리할 때 스택 되감기를 해야 하기 때문에 5000~10000 cycle에 달하는 오버헤드가 발생한다. 그래서 예외 자체를 비활성화하는 경우도 허다하나 표준 라이브러리를 사용하는 이상 예외를 피할 수 없다는 단점이 있다.

러스트 개발자들도 에러 처리가 귀찮다는 것을 알기 때문에 편의성을 높여주는 기능들이 러스트에 되어 있다. 예를 들면, 다음과 같이 함수를 호출해 발생한 에러를 그대로 반환할 수도 있다.
use std::error::Error;

fn foo() -> Result<(), Box<dyn Error>> {
    bar()?;         // bar()에서 에러가 발생하면 이를 즉시 반환한다.
    Ok(())
} 

또한 러스트는 타입 유니온[22]이 없기 때문에 여러 에러를 한데 묶기 위해선 enum을 별도로 선언해야 한다. 이러한 귀찮음을 막고자 thiserror등 매크로 기반 서드파티 크레이트도 존재한다.


3.3. 편리한 열거형[편집]


여타 언어들의 enum은 단순히 상수들에 이름을 붙인 것이지만, Rust에서는 각각의 열거 값을 구조체나 튜플로 정의하여 내부에 값을 포함하는 것이 가능하다. 이때 해당 값의 소유권이 이동된다. 다양한 동작을 할 수 있도록 구조체나 튜플처럼 메서드를 추가하거나 트레이트를 구현할 수도 있다. enum의 크기는 열거 값 중 가장 큰 것의 크기에 따라 컴파일 타임에 결정된다.[23]

간단한 예시는 다음과 같다.
enum Action {
    Hello(String),
    Call { country: u16, number: String },
    ThankYou(String, i32),
    Unknown,
}

let action = Action::Hello(String::from("namu"));

match action {
    Action::Hello(name) => println!("hello {} again !", name),
    Action::Call { country, number } => println!("call at +{}{}", country, number),
    _ => println!("sorry, i do not know that action :("),
}

이렇게 enum을 설계함으로서 enum의 유연성을 극대화시켰으며, 그 결과로 러스트에서는 다른 언어의 switch에 해당하는 match 구문이 자주 쓰인다. match 구문을 이용하면 열거 값에 따라 분기하면서 구조 분해까지 한 번에 할 수 있다.

이때 주의할 점은, match는 가능한 모든 분기를 검사하도록 강제된다는 것이다(exhaustiveness check).
enum State {
    Running,
    Waiting,
    Terminated,
}

let process_state = State::Waiting;

match process_state {
    State::Running => do_something(),
    State::Terminated => cleanup_process(),
    // error[E0004]: non-exhaustive patterns: `Waiting` not covered
    // State가 가질 수 있는 값의 종류는 3가지이지만, 위 코드에서는 2가지밖에 검사되지 않았다.
    // 에러를 없애려면 나머지 경우를 처리하는 코드를 추가한다.
    // State::Waiting => todo!()
    // 남은 패턴을 한번에 처리하려면 _(wildcard pattern)을 사용한다.
    // _ => todo!()
};
러스트 컴파일러는 enum 타입에 대한 정보를 가지고 주어진 match 식이 가능한 모든 경우를 체크했는지 컴파일 타임에 검사한다. 만약 컴파일러가 충분히 똑똑하지 못했다면, 위 코드는 대기 중인 프로세스를 처리하지 못하고 루프를 반복해서 돌기만 하는 버그를 일으켰을 수 있다. 덕분에 enum과 match 구문을 효과적으로 사용하는 러스트 개발자는 버그를 미연에 방지할 수 있으며, 조금 더 확신을 가지고 개발에 집중할 수 있다.

특히나 이
match
구문은 함수형 프로그래밍 언어 사용자들이라면 익숙할 패턴 매칭 기능이다. elixir만큼 자유로운 매칭까지는 아니지만 범위 매칭, @ 바인딩, 매치 가드 등 편리하고 핵심적인 기능을 제공한다. Rust에서는 함수형 언어의 개념 중 하나인 모나드를 enum을 통해 간단하게 구현할 수 있으며, 그 예로는
Result<T, E>
,
Option<T>
등이 있다. 다른 언어들은 언어 차원에서 모나드를 지원하기 위한 특수 문법이 존재하지만, Rust에서 이들은 enum으로 표준 라이브러리에 정의되어 있다.

enum Option<T> {
    None,
    Some(T),
} 
Some이 T를 소유하기 때문에 이를 앞서 본 것과 동일한
match
문으로 검사하여 값이 있는지 없는지를 실수 없이 검증할 수 있다.


3.4. 트레이트[편집]


Rust는 객체 상속을 제공하지 않는다. 상속을 사용하면 복잡해지고 유연성이 떨어지기 때문이다. 대신 트레이트(인터페이스)에서 상속을 허용하여 유연성과 재사용성을 동시에 확보했다. 트레이트는 객체의 특성을 나타냄과 동시에 그 특성과 관련한 인터페이스[24]를 제공한다. 객체에
Clone
트레이트를 구현하면 복제가 가능함을 나타냄과 동시에
clone()
메소드가 제공되어, 객체의 특성과 메소드가 하나의 묶음으로써 존재하게 된다.

트레이트는 객체와 달리 변수를 가질 수 없으며 상속이 가능하다. 그리고
impl T for A
블록을 작성해서 어떤 클래스에 트레이트를 구현하게 되면, 구현된 메서드를 사용할 수 있다. 트레이트에 메서드의 기본 구현이 있는 경우, 기본 구현을 그대로 사용하도록 하는 것도 가능하다.

트레이트가 가지는 중요한 역할 중 하나는, 제네릭 인자에 트레이트를 써서 인자로 들어갈 타입에 필요한 조건(특성)을 붙이는 것이다.[25] 이는 다른 언어의 덕 타이핑과 유사한 개념이며, Iterator를 구현한다면 (타입은 잘 모르겠지만) 순회 가능할 것이고, Fn을 구현한다면 클로저일 것이고, Ord를 구현한다면 (역시 구체적인 타입은 몰라도) 크기를 비교 가능한 값임을 알 수 있다. 예를 들어 값 세 개를 오름차순으로 정렬하는 제네릭 함수는 이렇게 만들 수 있다.

덕 타이핑에서 한 단계 더 나아가, '어떤 트레이트를 구현하는 타입이라면 이런 연산을 할 수 있을 것' 이라고 정의한 것이 바로 연산자 오버로딩(operator overload)이다. 가령 복소수를 표현하는 구조체를 만들고 Add를 구현해 복소수끼리의 덧셈을 정의했다면 일반 숫자를 다루듯이
+
연산자를 사용할 수 있다. 역참조 연산자인
*
또한 오버로딩 할 수 있으며, DerefDrop트레이트를 수동으로 구현해서 스마트 포인터를 만들 수 있다.

객체지향이 보편적으로 쓰이는 게임이나 시뮬레이션 등에서 이러한 특징은 큰 약점이 될 수도 있다고 생각할 수 있으나 그렇지는 않다. 라이브러리 단에서 procedual macro를 제공하여 자동적으로 구현이 되도록 하거나 trait 기본 구현을 제공할 수 있다. 이를 통해 인터페이스의 재사용성뿐만 아니라 객체 구현의 재사용성도 어느 정도 달성이 가능하다.


3.4.1. 특수한 트레이트[편집]


몇몇 트레이트(Trait)는 컴파일러에게 특수한 취급을 받는다. 이중 몇몇은 특수한 성질을 나타낼 때 쓰이고 몇몇은 문법적 추가 요소(Syntactic Sugar)로서 작동한다. 이들 중 대부분은 std::marker, std::ops에서 찾을 수 있다. 이때 marker는 말 그대로 내용이 비어 있어 아무 동작도 안 하지만 trait bound에 사용할 수 있는 트레이트를 말한다.

  • Send
    : 타입
    T
    가 스레드 경계선을 넘나들 수 있음을 명시한다.
  • Sync
    : 타입
    T
    의 참조가 스레드 경계선을 넘나들 수 있음을 명시한다.
    T: Sync
    &T: Send
    와 동치이다. 기본적으로 모든 primitive type (u8, char 등) 은 Send/Sync 이고 이들을 사용한 파생 타입 또한 Send/Sync 이기 때문에 일반적으로 신경쓰지 않아도 변수를 자유롭게 스레드 간에 공유할 수 있다. 포인터와 같이 Send/Sync가 구현되지 않는 타입을 포함하는 경우 Send/Sync가 자동으로 구현되지 않는다. 개발자가 unsafe를 명시하여 구현할 수는 있으며 이 경우 스레드 안전성은 개발자가 책임을 갖게 된다. (예시:
    Cell
    ,
    AtomicUsize
    )
  • Clone
    : 완전한 복제를 제공하도록 하는 트레이드. 이게 없는 객체는
    =
    으로 이동 연산만 가능하다.
  • Copy
    : 필요한 경우
    a = b
    에서 이동 연산 대신 복사 연산을 수행하도록 하는 트레이트. 단순히 타입의 값을 복사하는 것으로 복사가 가능한 타입에만 자동으로 구현되며 이를 수동으로 구현하는 것은 불가능하며, 모든 자식 필드가
    Copy
    를 구현하는 경우만 Copy매크로를 통해 derive할 수 있다. 모든 primitive type은 기본적으로 이 트레이트를 구현하고 있다. 일반적인 역참조의 경우 소유권 때문에 이동하지 못하거나
    ToOwned
    를 써야 하는 반면,
    Copy
    를 구현하는 타입은 역참조만으로 값을 얻을 수 있다(정확히는 복사된다). 이는
    Box
    만 예외로, T가 Copy가 아니더라도 역참조로 소유권을 반환하는 유일한 경우이다.
  • Deref
    /
    DerefMut
    :
    *
    (역참조) 연산을 구현하는 트레이트. 스마트 포인터를 만들 때 유용하며, 따라서
    Deref
    를 구현하는 객체는 거의 대부분의 경우에
    &T
    와 동일하게 사용할 수 있다. 또한
    Deref
    를 구현하는 타입을 함수로 전달할 때, 타입이 맞지 않으면 러스트 컴파일러는 암묵적 자동 역참조(implicit deref coercions)를 사용해 해당 타입을 재귀적으로 역참조한다. 예를 들어, 슬라이스만 받는 함수를 벡터의 참조를 통해서도 호출할 수 있는데, 이는
    &Vec
    deref
    &[T]
    타입을 반환하기 때문이다. #
  • Sized
    : 컴파일 시 타입의 크기를 알 수 있는 경우를 명시. 기본적으로 모든 Trait bound에는
    Sized
    가 암묵적으로 붙기 때문에 크기를 알 수 없는 타입을 받으려면
    ?Sized
    를 사용해야 한다.
  • Unpin
    :
    Pin
    을 통해 포인터가 고정된 타입을 이동 연산할 수 있는 경우를 명시.
  • FnOnce
    /
    FnMut
    /
    Fn
    : 클로저 트레이트. 일반적인 트레이트와는 다르게
    Fn(T, U, V...) -> R
    처럼 튜플 형태의 인자 타입과 리턴 타입을 가진다. 함수 포인터와는 차이가 있는데, 함수 포인터는 컴파일 타입에 이미 모든 크기를 알 수 있는 포인터일 뿐이지만 클로저는 어떤 값을 캡처(capture)할지 미리 알 수 없기 때문에 크기를 알 수 없고 따라서 트레이트를 사용한다. 호출했을때 자기 자신을 소비(consume)하는 클로저라면
    FnOnce
    ,
    &mut self
    를 받아 내부 상태를 변경한다면
    FnMut
    도, 자기 자신을
    &self
    로만 참조한다면
    Fn
    도 구현한다. 주로 고차 함수를 만들 때 쓰인다.

이 외에
for
문에서 쉽게 사용될 수 있도록 하는
Iterator
/
IntoIterator
등의 trait 등도 존재한다.

3.5. 하이지닉 매크로[편집]


Rust의 매크로는 다음의 특징을 가지고 있다.

  • 매개 변수의 이름으로 인해 텍스트 중복이 되지 않도록 자동 처리된다.
  • 매크로에 입력될 매개 변수의 타입을 지정할 수 있다.
  • 메타 프로그래밍이 가능하다.

C/C++에서의 매크로는 단순한 문자열 치환이다. 예를 들어 MUL5(x) (x * 5)라고 한다면
MUL5(3)
(3 * 5)
로 치환될 것이다. 이것은 문맥에 따라 수많은 잠재적인 문제를 만들 수 있다. Rust의 매크로는 LISP의 하이지닉 매크로 개념을 상당수 차용했다. 컴파일러가 단순한 텍스트 중복이 문제가 안 되도록 알아서 잘 처리한다.

C/C++의 매크로에서는
MUL5(3)
에서
3
이 곱셈이 가능한지, 실수인지 이런 여부를 판단할 수 없다. Rust의 매크로 시스템에서는
MUL5(x)
에 들어온 인자가 변수의 이름인지, 문자열인지, 네임스페이스인지, 람다 함수인지 등을 알 수 있다.

또한, Rust의 매크로는 메타 프로그래밍을 지원한다. 메타 프로그래밍은 컴파일 타임에 모든 것이 결정되는 프로그램을 작성하는 것으로써, 입력되는 매개 변수에 따라 넣을 코드를 결정하거나 미리 계산하도록 할 수 있다.

Rust에서 제네릭과 메타 프로그래밍이 분리되어 지원되는 것은, C++에서는 템플릿 문법이라는 하나의 도구를 이용해 일반화 프로그래밍(제네릭)과 메타 프로그래밍 두 가지를 지원하는 것과 대비되는 점이다. 물론, Rust에서 메타 프로그래밍이 매크로로 분리됐다고 해도, 메타 프로그래밍 자체가 여전히 어려운 것은 변함이 없다.


3.6. 비동기 프로그래밍[편집]


https://areweasyncyet.rs/

Rust는 언어 차원에서 비동기 프로그래밍을 지원하고 있으며, Python, C\# 같은 다른 언어에서 사용되는 async/await 구조의 비동기 구문을 비슷한 형태로 사용할 수 있다.#

다른 언어에서 흔히 사용되는 비동기 프로그래밍 패턴 중 Future 개념은 코루틴[26]인 대표적인 예이다. 이러한 개념을 구현한
Future
# 트레이트와
async
/
await
문법을 사용하면 되는데, Javascript 와 같은 Promise 기반의 언어와 달리 비동기 함수를 실행할 실행자(Executor)가 별도로 존재한다. Promise 객체는 객체가 생성됨과 동시에 자동으로 스케줄링이 되지만, Future 객체는 객체 생성 순간에는 아무것도 실행되지 않는다. 실행자는 실질적으로 작은 스케줄러 역할을 수행하며 이에 대한 구현은 서드파티에게 맡긴다. 현재 tokio가 가장 널리 쓰이는 실행자 구현체이다[27].

Future
async
함수 선언을 통해 자동적으로 구현된다.
Future
는 기본적으로 스레드 간에 이동될 수 있기 때문에 어떤 스레드에서 일시 정지됐다가 다른 스레드에서 재개하는 것도 가능하다. 그러나
async
함수에서 포인터와 같이
Send
가 구현되지 않은 타입을 다루게 되면,
Future
Send
가 구현되지 않으며 비동기 프로그래밍에 상당한 제약사항이 발생한다.# 이러한 문제를 해결하려면
async
함수에서 그러한 타입을 직접 다루지 않고 별도의 함수에서 처리하도록 해야 한다.

Rust에서 비동기 프로그래밍을 시작해보고 싶다면 Asynchronous Programming in Rust[28]를 읽어보는 것을 추천한다. 아직 미완성이기는 하지만 Rust 언어에서 코루틴과 실행자를 구성하는 대략적인 구조를 이해하는데 도움이 되며, 부족한 내용은 표준 라이브러리 API 명세을 참고하거나 구글 검색으로 관련 내용을 찾아보면 보충할 수 있다.


3.7. 제네릭[편집]


Rust에서도 C++, C\#, Java 등 대중적인 정적 타입 프로그래밍 언어들에서 흔히 제공하는 일반화 프로그래밍(Generic Programming) 패러다임을, 제네릭이라는 기능으로 제공하고 있다.

다만, Java나 C#이 제공하는 제네릭과, Rust가 제공하는 제네릭(그리고 C++ 템플릿)은 언어 설계 단계에서 채택한 정책이 서로 크게 다르다.

예를들면, Java의 제네릭은 컴파일했을 경우 단 하나의 코드가 생성된다. 이후에 여러가지 다른 타입의 인자가 주어졌을 경우, 각 인자의 타입을 지워서(type erasure) 이미 생성해둔 하나의 코드에 적용시키는 방식이다. 이에 반해서, C++ 템플릿은 컴파일했을 경우 해당 프로그램 전체의 사용 패턴을 확인해서 적용된 모든 타입의 인자에 해당하는 코드가 각각 따로 생성된다.

즉, Java의 경우에는 코드 생성 과정이 단순하고 생성된 코드 크기가 훨씬 작다는 장점을 선택한 대신 매번 실행시에 type erasure 과정을 거치는 오버헤드를 감수하기로 한 것이고, C++의 경우에는 매번 실행시에 오버헤드가 전혀 없다는 장점을 선택한 대신에 생성 코드 크기가 커질 수도 있고 컴파일 타임이 길어질 수도 있다는 것을 감수하기로 한 것이다. Rust는 C++의 방식을 채용하므로, 런타임 성능을 강조하고 컴파일 타임을 약간 희생하는 방식이다.

하지만, Rust의 제네릭과 C++의 템플릿 방식 사이에도 차이점이 존재한다. Rust의 경우 위의 다른 항목에서 설명한 트레이트 덕분에 코드 생성에 필요한 정보가 컴파일러에게 보다 많이 제공될 수 있다. 이를 통해 Rust 컴파일시에 C++보다 더 많은 체크와 최적화가 이론적으로 가능하다.[29]


4. 개발 도구별 특징[편집]



4.1. 안전하고 강력한 컴파일러[편집]


안전성을 위해 컴파일에 최대한 오류가 많이 걸러지게 설계된 Rust의 특성상 컴파일러가 매우 강력하다. 대부분의 예외나 에러는 컴파일러에서 잡히고, [30] 오류를 잘 검출하는 것뿐만 아니라, 오류가 난 이유와 과정, 해결 방법 제안 등 컴파일 단계에 많은 정보를 제공한다. 따라서 일단 컴파일이 된다면 런타임 에러가 상대적으로 덜 발생하게 된다.

컴파일 단계에서 여러 const 관련 기능, 상술한 C, C++ 라이브러리와의 정적 링크를 위한 코드 생성, 극도로 strict한 타입 관리, 에러 발견 등 최대한 많은 작업을 처리하려고 하기 때문에 컴파일 속도가 느린 편에 속한다. 하지만 이것은 컴파일러에 도입한 incremental compile[31]을 통해 부분적으로 해결했다.

Rust는 C나 C++의 코드를 오버헤드 없이 코드에 이식할 수 있는데, 그 Cross-Language LTO라는 기능도 이 컴파일러의 기능이다.

4.2. 편리한 패키지 관리 도구[편집]


업로드, 의존성 설정, 버전관리 등 다양한 기능을 가지는 패키지 관리 프로그램 Cargo가 있다. 모듈들은 크레이트(Crate)라고 하는 단위로 묶여서 Crates.io (Rust 패키지 저장소)를 통해 실행 파일이나 라이브러리로 배포될 수 있으며, Cargo를 통해 빌드 및 패키지 배포를 자동화하고 필요한 라이브러리를 Cargo를 통해 자동으로 다운로드받을 수 있다.

주석을 통한 자체 문서화 기능도 있어서, 특수 주석을 통해 실제 사용 예시와 함께 소스코드 내에 문서화를 해주면 패키지를 업로드 할 때 패키지 주소에 자동으로 문서가 만들어진다!
이 문서 시스템을 적용한 대표적인 예는 Rust의 std가 있으며, 이러한 편리한 문서화 기능으로 인해 rust의 std문서는 상당히 잘 구성되어 있다.

Rust에서 제공하는 다양한 툴을 실행하는 기능도 가지고 있다. 기본적인 build, run, check[32] 명령어와 함께, Rust에서 추가로 제공하는 fmt[33], clippy[34], miri[35]등을 설치하여 사용할 수도 있다.

4.3. 텍스트 에디터 지원[편집]


IntelliJ IDEACLion의 Rust 플러그인은 IntelliJ 플랫폼답게 히스토리 기반 코드 컴플리션, 리팩토링, 디버깅, Cargo 패키지 추적 등 여러가지 편의성을 제공한다. Visual Studio Code는 IDE는 아니지만, 공식으로 Rust 언어용 플러그인을 제공한다. 몇몇 다른 언어와 마찬가지로 Rust Language Server와 통신하는 방식으로 동작하며, Cargo 프로젝트 내의 모든 문제나 오류를 바로 쉽게 확인할 수 있다. Visual Studio Code를 사용한다면 일반적으로 rust-analyzer[36] 확장을 추천한다. 본래 비공식 플러그인이었으나 22년 2월부터 러스트 조직에 합류하여 러스트 공식 플러그인이 됐다. 시각적으로 타입 힌트를 표시하는 등 기존의 플러그인보다 더 편리한 기능들을 제공한다. Atom도 Rust를 플러그인 형태로 지원하고 있다.

9월 13일 JetBrains 사에서 Rust Foundation에 가입했음을 알림과 동시에 Rust 전용 통합 개발 환경RustRover를 공개했다. 다운받는 곳
아직은 얼리 억세스 단계이며 상용 IDE로 기획되고 있다. 2024년 9월까지 출시하는 것을 목표로 하고 있으며 그 전까지는 자유롭게 무료로 사용할 수 있다.
하지만 그 전까지 무료로 GitHub와 JetBrains marketplace에서 오픈소스로 제공했던 러스트 플러그인은 최신 버전의 IDE와의 호환성은 계속해서 제공하지만 더 이상 버그 수정이나 새로운 기능 추가를 제공하지 않을 예정이라고 밝혔다.

5. 주목받고 있는 언어[편집]



5.1. 개발자들이 가장 좋아하는 언어[편집]


2015년부터 스택 오버플로우 설문조사에서 매년 가장 좋아하는 언어 중에 하나로 선정되고 있다. 2015년에는 3위에 진입했다가, 2016년부터 매년 연속 1위를 달성했다. (2017년, 2018년, 2019년, 2020년, 2021년,2022년 설문조사 결과)

5.2. 대기업에 주목받고 있는 언어[편집]


아마존, 구글, 마이크로소프트, 페이스북, 모질라, 리눅스 재단, 디스코드, 드롭박스, npm 등 IT 관련하여 유명한 기업이나 재단이 자사 서비스에 Rust를 적극적으로 활용하고 있다.#

Visual Studio를 개발한 마이크로소프트가 Rust를 메모리 안정성이 좋고, C 및 C++를 대체할 수 있는 시스템 프로그래밍 언어라고 말했다. 심지어 Rust의 주목성에 탐이 났는지 Rust 같은 언어를 자체적으로 만들겠다는 소식이 나왔을 정도.

실제로 MS에서도 러스트를 밀어주기 위해 자사 소프트웨어 기술지원 사이트인 Docs에 Rust 교육과정을 추가했으며 러스트 개발과 관련된 기초적인 자료를 올려놓기 시작했다. Docs 특성상 MS에서 만든 .NET계열이나 Azure관련 자료가 많았는데 MS가 주도하지 않는 언어가 추가됐다는 사실 자체로도 매우 놀랍다고 볼수있다.

구글또한 MS와 마찬가지로 Rust와 같은 언어를 자체적으로 만드는 시도를 해서 Carbon이라는 언어를 공개했다.

5.3. 운영체제 개발 언어[편집]


C++은 그 불안함 때문에 도입을 꺼리거나 일부분만 사용되어왔는데 Rust가 등장하자 C++도입을 꺼리던 리눅스에서도 도입됐고, 윈도우나 안드로이드처럼 울며 겨자먹기로 C++를 도입했던 운영체제들도 C++를 빠르게 밀어내고 러스트로 대체 중이다.

마이크로소프트 윈도우NT 커널에서도 11.0 버전부터 도입되기 시작했다.#

리눅스리눅스 커널에서도 6.1 버전부터 러스트를 도입하기로 결정했다.#

리눅스 파생형인 안드로이드에서는 도입이 더 빨랐다.[37]

2021년 4월 6일 구글 보안 블로그에 따르면 안드로이드 오픈소스 프로젝트(Android Open Source Project, AOSP)가 안드로이드 OS개발 언어로 Rust를 추가하기로 했음을 밝혔다.[38] 이는 기존에 주로 CC++로 이루어지던 안드로이드 OS의 하단부 개발과 관련하여, 기존의 코드를 갈아 엎겠다는 것이 아니라 새로운 프로젝트들을 Rust로 작성하는 것도 허용하고 장려하겠다는 뜻이다.[39][40] 2022년 12월 구글 보안 블로그에 안드로이드에 Rust를 도입한 이후에 얻은 성과를 공유하는 글이 올라왔다. #

Asahi Linux의 GPU 드라이버도 러스트 기반으로 개발되어 현재 OpenGL ES 3.1까지 완벽하게 지원한다. C로 처음부터 개발하자니 너무 복잡하고 잠재적인 문제점들도 많아 당시 막 리눅스에 도입되기 시작한 러스트로 개발하는 모험을 감행했고, 결과적으로 드라이버 작성을 시작한지 두 달도 안되어서 GNOME 데스크탑을 안정적으로 실행하는데 성공했다.

6. 사용 현황 및 전망[편집]



6.1. 전망[편집]


오버헤드 없는 안전한 메모리 관리가 가능하다는 점에서 시스템 프로그래머들이 주로 선호하는 편이다. 모질라 재단에서 개발한 만큼, 파이어폭스에서는 상당 부분을 Rust로 대체했고, 구글은 차기 운영체제인 퓨시아에서 Rust를 사용중이며, 아파치의 mod_ssl 모듈의 안정성을 개선하기 위해 Rust를 사용할 것이며 지원하겠다고 밝혔다. 또한 머신러닝 라이브러리인 Tensorflow Rust 또한 지속적으로 지원하고 있다. 링크 .
페이스북도 내부 시스템 일부에 Rust를 적용한 상태라고 한다. 레딧에서는 백엔드 개발을 담당할 Rust 엔지니어를 구인한 바 있다. # 이 언어로 Redox란 운영체제도 개발되고 있다. 또한 페이스북의 암호화폐 리브라에서도 사용되고 있다. npmCloudflare는 기반 언어를 C에서 Rust로 교체했다. 2020년 기준 Discord가 서버와 클라이언트단 언어를 Golang에서 Rust로 교체했다. 차세대 자바스크립트 런타임인 Deno도 시스템 바인딩을 Rust로 작성했다.

그러나 전체적으로 보면 여전히 Rust의 사용률은 미진한 편이다. TIOBE 순위에서는 순위가 떨어지는데, 이는 소프트웨어 인프라가 기존 언어로 대부분 갖추어져 있고, 전문 인력을 구하기 쉽지도 않으며, 라이브러리에 대한 지원이 적기 때문으로 보인다.# 순위는 5년동안 꾸준히 상승하고 최근 ScalaKotlin을 제치고 올라가고 있지만, 여전히 대부분의 기업은 시스템 프로그래밍에 거의 C/C++만을 사용하고 있다.

다만, 2019년 스택 오버플로우 개발자 설문조사에서 무려 파이썬자바스크립트를 제치고 83.5%의 높은 선호도로 "개발자들에게 가장 사랑받는 언어" 1위를 차지했다는 점이나 구글 트렌드나 각종 랭킹 사이트에서의 수치가 장기적으로는 증가하고 있다.

"Rust는 소유권과 수명을 직접 지정해줘야 해 불편하고 문법이 어렵고 기능이 많아 배우기 힘들다"는 주장이 있으나, "Rust의 주 타깃이 되는 유저들은 Rust보다 훨씬 더 어렵고 불편한 C/C++를 쓰고 있기에 문제가 되지 않는다"는 반론도 있다. 페이스북이 공개한 자료에 따르면 자사 엔지니어가 Rust를 능숙하게 익히는데 두 달이면 충분했다고 한다.

C++ 사용자들의 가장 큰 불만은 난이도가 아니라 메모리와 관련한 실수를 하기 매우 쉽다는 것이다. Rust의 설계 목표가 비교적 안전한 시스템 프로그래밍 언어인 만큼, Rust에서 메모리 안정성을 위해 문법을 강제함으로써 발생하는 불편함과 C++에서 메모리 버그를 코드 리뷰와 디버거로 일일이 찾아내는 수고는 비교가 안 된다. 구글과 마이크로소프트에서는 Rust를 사용할 경우 자사 제품의 보안 버그 패치 비용의 70%를 절감할 수 있을 것이라는 내용의 문서도 공개했다. 구글/마이크로소프트/인텔은 이미 Rust의 개발에 깊숙이 관여하고 있으며, 각종 컨퍼런스에서 공개적으로 이를 언급하고 있다. C++11부터는 C++98와 달리 Raw pointer를 직접 사용하는 대신 컨테이너나 스마트 포인터의 사용을 적극적으로 권장하고 있어 충분한 메모리 관련 안전성을 유지하면서 코드의 작성이 가능하려는 변화를 하고 있다.

Rust는 모질라 재단을 통하여 개발이 시작된만큼 모질라와 상당한 연관이 있는데, 2020년 8월 기부금으로 움직이는 모질라 재단이 코로나로 인해 자금 융통이 어렵다는 이유로 250명을 감축했으며, 이로 인해 Rust개발팀이 상당한 피해를 입었을 거라는 추측이 돌았다. # 그러나 일각에서는 Rust가 이미 모질라재단 소속의 기여보다 외부의 기여가 많아진 상황이라, 큰 영향은 없을 것으로 보기도 했다. 실제로는 Rust 개발팀이 아니라 Rust를 이용해 파이어폭스Gecko를 대체할 렌더링 엔진을 개발하려던 Servo 프로젝트가 리눅스 재단으로 인계되며 피해를 입은 것으로 드러났다.

2020년 9월 TIOBE에서 18위를 달성했다.

AWS는 다른 기업보다 Rust에 관심이 많은데, 실제로 Rust를 사용하여 Amazon S3(Amazon Simple Storage Service), Amazon Elastic Compute Cloud(Amazon EC2), Amazon CloudFront, AWS Lambda(서버리스) 등 서비스를 제공한다. 최근에는 러스트로 작성된 Linux 기반 컨테이너 운영체제 보틀로켓(Bottlerocket)을 출시했다. 또한 아마존 EC2 팀은 Nitro Enclaves와 같은 민감한 애플리케이션을 포함한 새로운 AWS Nitro 시스템 컴포넌트의 선택 언어로 Rust를 사용한다. 또한 2020년 11월 아마존(AWS 클라우드 팀)이 Rust 컴파일러의 공동 리드를 맡았던 펠릭스 클록(Felix Klock)을 개발자로 영입했다. 영입을 이끈 Matt Asay는 트위터를 통해 AWS에서도 Rust가 사용되고 있으며, 안정성과 퍼포먼스에 상당한 기여를 하고 있다고 밝히기도 했다. Matt Asay 트위터. 또한 AWS는 2019년에 스폰서십을 시작으로 2021년 2월에 발족한 러스트 재단에 초기 회원사가 됐다. 이외에도 Rust 개발자를 적극적으로 구인하는 중이라고 밝히기도 했다.관련 기사 한 편으로는 이를 모질라재단의 감축으로 인한 효과라고 보는 시선도 있다.

한국인 개발자에 의해 개발된 TypeScript 컴파일러인 SWCshopify, Next.js# 등 큰 생태계를 가진 플랫폼에서 정식 채용되면서 또다시 관심을 받았다. 또한 2020년 이후로 WebAssembly가 적극적으로 사용되면서 서버사이드뿐 아니라 브라우저에서도 Rust를 활용할 여러 방법들이 제시되는 등 웹개발 분야에선 끊임없이 관심의 대상이 되고있다.

Ruby의 컴파일러인 YJIT의 코드베이스가 C99 에서 Rust로 대체될 예정이다. Ruby의 핵심개발자들이 포팅에 참여해서 작업해 2022년 4월 20일, 포팅이 완료됐으며, 다른 기여자들의 리뷰를 위해 소스코드가 공개됐다. 해당 Github 풀 리퀘스트

Linux 커널 6.1부터 일부 구성요소 한정으로 Rust로 대체될 예정이다.#

Microsoft에서 기존 윈도우 코드를 Rust로 새로 짜는 중이라고 한다.#

2023년 들어 Rust로 GUI 앱을 만들기 위한 라이브러리와 프레임워크가 많이 생겨나고 있다. Flutter를 프론트엔드로 연결하는 Rinf나 GTK를 프론트엔드로 연결하는 GTK4-RS가 대표적이다.

6.2. 기존 언어와의 비교[편집]



6.2.1. Rust vs C++[편집]


Rust의 출시 배경은 C++의 안전하지 않은 메모리 관리로 인해 숙련되지 않은 프로그래머가 작성시에 오류나 취약점이 발생할 위험이 매우 높아진다는데 있다. 전 세계 수십억명이 사용하고 있는 윈도우크롬은 개발 과정에서 코드 리뷰 과정을 여러 번 거칠 것임에도 불구하고 매년 CVE로 분류되는 수십개의 메모리 버그를 해결하기 위해 계속 패치 및 긴급 패치를 제공하고 있다는 점을 알고 있을 것이다. 현실적으로 C/C++ 언어에서 메모리 버그를 완전히 잡아내기란 어렵다. 따라서 Rust나 가비지 컬렉터 언어가 제공하는 메모리 안전성은 프로그래머에게 안정감을 제공함과 동시에 보안성 및 생산성 측면에서 상당한 이점을 가져다 주며 이것이 C/C++와의 큰 차이점이라고 할 수 있다.

C++를 사용해오던 많은 개발자가 Rust에 대해 처음 접할 때 Rust의 메모리 안전성은 단순히 스마트 포인터를 통해 달성할 뿐이라고 잘못 이해하는 경우가 있다. Rust는 RAII와 더불어 static analysis를 통해 모든 객체의 수명과 참조자의 유효성을 검증할 수 있도록 설계됐다. 컴파일러는 참조자의 유효성을 borrow checker를 통해 보수적으로 검증하며, 검증하기 어려운 경우는 아예 에러를 반환한다. # 현재까지 borrow checker의 결함으로 인한 보안 취약점은 단 하나도 발견되지 않았다. C++ 위원회에서도 Rust의 이와 같은 설계의 이점을 인식했는지 C++ Lifetime Profile이란 제안서를 내놓았다.

C++ 창시자는 Rust의 메모리 안전성은 C++에 비해 특별한게 없다고 주장했지만, 그의 주장이 사실과 거리가 멀다는 것이 통계로 드러나고 있다. 구글은 지난 수년간 이미 Android에서 C/C++를 다른 메모리 안전 언어로 대체함으로써 보안 취약점이 70% 이상 감소했다는 통계를 보여주었다. NSA가 공개한 가이드라인에는 C/C++가 메모리 안전 문제로 보안 취약점에 노출되기 쉽기 때문에 GC 언어나 Rust와 같은 메모리 안전 언어를 사용할 것을 권장했다. #

Rust는 D언어와 달리 이미 성공적인 상업적 사용 사례가 상당수 존재한다. Rust는 구글이나 아마존이 클라우드에 적극적으로 사용하고 디스코드가 Go로 작성된 서버를 대체하며 리눅스 커널 모듈을 작성하기 위한 언어로 추가되기도 했다. "C++를 대체할 것인가"와 관계없이 안전하고 빠른 고생산성 언어로써 충분히 메리트가 있다는 점을 알 수 있다. 기존의 레거시 코드를 강제로 세대교체할 이유는 없다. 앞으로 세상에 내놓아지는 물건들이 Rust, Swift 등 이쪽 계열의 현대적인 언어들로 만들어진다면 그것만으로 Rust의 향후 언어 생태계 조성에 대한 잠재성은 충분하다.

C++의 단점을 거의 지워버리듯이 한 Rust는 C++을 충분히 대체할 수 있을듯 싶지만, 아직 C++에 비해서 점유율이 많이 밀린다.

  1. C++로 구현된 기존의 인프라
C++는 수십년간 존재하던 언어로서 기존의 많은 프로그램이 C++ 기준으로 작성되어 있고 유지되고 있다. 대부분의 라이브러리는 다른 언어에 바인딩을 제공하는 경우가 많기 때문에 큰 문제가 되지 않지만, Qt와 같이 다중 상속을 사용하는 등의 복잡한 라이브러리는 바인딩이 매끄럽지 못하다. 그리고 대다수의 하드웨어들은 리소스 접근을 C/C++ 언어로 지원한다.
  1. 아직은 생소한 언어
대부분의 기업이나, 정부 기관 입장에서 Rust 언어는 아직은 생소하다. 프로그래밍 언어의 경우 대세가 바뀌기 까지 짧게는 10년[41]이 걸린다. C++로 작성된 기존의 프로그램을 유지 보수만 하고 있는 마당에 Rust 언어로 뭔가 덧붙이거나 다시 작성하지는 않을 것이다. 아직도 MFC 같은 레거시 라이브러리를 쓰는 경우도 많다.
  1. 엄격하여 발생되는 불편함
Rust 언어는 모든 객체의 수명과 참조자의 유효성을 정적 분석을 통해 컴파일러가 검증할 수 있도록 설계됐으며 이를 통해 메모리 안전성을 보장한다. 그런데 복잡한 참조자의 경우 lifetime parameter를 개발자가 직접 지정해줘야 할 때가 많다. 초보 개발자들은 이때 발생하는 에러와 복잡성에 골치를 앓게 된다. 그리고 철저한 에러 처리 규칙으로 인해 실질적으로 에러 발생 가능성이 없는 부분에도 컴파일러가 에러 처리를 하라는 경고 메시지를 출력한다. 다만 컴파일러가 원인을 이해하기 쉽게 설명해주고 수정안을 표시해주는 등 다른 언어들에 비해 비교적 친절한 편이다.


6.2.1.1. 결론[편집]

현재까지 Rust는 C++에 비해 사용자가 적어서 열세인 모습을 보이고 있다. 그러나 D언어와 달리 많은 기업에서 관심을 가지며 성공적인 도입 사례가 많은 만큼, Rust의 상대적 편의성과 안전한 코딩 방법으로 인해 장기적으로 볼 때 C++의 강력한 대안이 될 수 있다. 다만, 이미 검증된 기존 프로젝트를 뒤엎고 Rust로 재작성한다는 것은 매우 큰 개발비 비용을 초래하며 아무런 이득이 없기 때문에 지양될 것이며, Rust는 새로운 프로젝트에서 사용될 가능성이 높다.
다만 최근 구글 보안팀의 발표에 따르면, 장기적으로 보안이 필요한 부분에서는 잠정적인 위협이 존재하는 C++코드를 들어내고 대체하려한다라고 발표해서, 기존 코드들도 필요에 따라선 많은 회사에서 C++에서 Rust로 대체할 것으로 보인다.


6.2.2. Rust vs Carbon[편집]


Carbon구글C++를 대체하기 위해서 2022년에 발표한 프로그래밍 언어이다. 위에 상기한 바와 같이 Rust의 최대 단점은 C++로 쓰여진 수없이 많은 기존 인프라와 호환에 문제점이 있다는 점인데, 이를 보완하기 위해 "C++와 양방향 상호 호환"을 철학으로 삼고 있다. 뉴스 기사

다만 Carbon의 경우 제대로 된 컴파일러도, 심지어 인터프리터 조차도 아직 없기 때문에 현 상황에서 비교는 무의미하다.

파일:나무위키상세내용.png   자세한 내용은 Carbon 문서를 참고하십시오.



6.3. 사용 현황[편집]



6.3.1. Rust를 사용하는 기업 및 단체[편집]


  • Discord - 백엔드에 Go을 사용했지만 GC처리속도로 인해 러스트로 옮겼다#. 또한 클라이언트 단에서도 비디오 인코딩을 위해 사용한다고 한다.
  • Dropbox - 대표적으로 러스트를 사용하는 기업 중 하나. 파일 동기화 엔진을 모조리 러스트로 재작성했다고 한다#.
  • 리눅스 재단: Linux 커널 개발에 Rust를 도입했다. 리누스 토발즈도 30년간 리눅스 개발에 C++도입을 반대하고 C만 고집했지만 Rust 도입은 찬성했다.
  • 마이크로소프트: 러스트 재단 초기 맴버중 하나로 2020년대부터 윈도우 커널 개발에 C++ 부분을 Rust로 대체중이며 그 성과의 일부중 하나로 Windows용 소프트웨어 개발에 사용되는 Windows SDKRust 래퍼가 제공되고 있다.
  • npm - C 언어로 짜여 있던 레지스트리 코드를 러스트로 옮긴 후 성능이 향상됐다. 현재는 yarn과 성능이 대등하다.
  • Sentry: 실무에서 많이 쓰이는 에러 로깅 툴로, 소스맵 처리기의 성능 개선을 위해 Python서 Rust로 CLI를 교체했다.
  • 솔라나 - 러스트로 구현된 암호화폐. 자세한 내용은 해당 문서 참고.
  • 트위터 - 인메모리 캐시를 Rust로 재작성하여 기존 Memcached 대비 8배 성능 향상을 달성했다고 한다. #
  • 러스트를 사용하는 다른 기업들.

6.3.2. Rust로 제작된 것들[편집]



7. 비판[편집]


파일:나무위키상세내용.png   자세한 내용은 Rust(프로그래밍 언어)/비판 문서를 참고하십시오.



8. 도서[편집]



8.1. The book[편집]


러스트 프로그래밍 공식 가이드
The Rust Programming Language
파일:러스트 프로그래밍 공식 가이드.jpg
원서
한국어 번역서[42]
온라인 보기
한국어 번역문

이 책은 러스트의 공식 가이드이자 오픈소스 서적인 the book을 그대로 책으로 옮겨 출판한 것이다. 국내 출판은 제이펍이 맡았다.

국내 출판서의 번역과는 별개로 the book자체를 포크해 기여자들이 자발적으로 번역한 한국어 번역이 있다. 그 외 언어에 대한 번역은 이곳에서 찾을 수 있다.

책 자체가 하나의 Git 저장소이기 때문에 최신 내용들이 제때 업데이트 되며, 누구나 PR할 수 있기 때문에 오탈자 또한 적은 편이다. 반대로 출판 서적의 경우 2018 에디션을 기준으로 하기 때문에 대부분의 경우 문제는 없지만 2021에디션을 사용한다면 사소한 차이가 있을 수 있다. (deprecated된
...
표기법 등)
현재는 2021에디션을 기준으로 한 2판이 출간되며 버전 문제가 해소됐다. 개정판 번역서는 2024년 2월에 출시할 예정이라고 한다.#

여담으로 문서 툴로는 mdBook을 사용한다.


8.2. 그 외[편집]


이외에도 Rust in Action(번역서: 한 줄 한 줄 짜면서 익히는 러스트 프로그래밍)이나 중급자 이상을 위한 Rust for Rustaceans[43]과 같은 책들이 있다.


9. 기타[편집]


  • 현 시점에서는 C의 C99나 JavaScript의 ES2015같은 안정되고 문서화된 표준이 아직 없다는 비판 의견이 있다(#). 다만 반론도 있고(#), Ferrocene(구 Sealed Rust)같은 프로젝트도 있다.


10. 관련 링크[편집]



[1] 첫 발표 기준으로, 1.0 정식판 기준으로는 Rust가 3년 늦게 나왔다.[2] 언어에 마스코트가 왜 필요한지 의문이 들 수도 있는데, Go, Zig 등을 비롯한 비교적 최근에 나오는 몇몇 언어들은 고유한 마스코트가 따로 존재한다. 언어는 아니지만 유니티 엔진도 유니티짱이라는 마스코트가 존재한다.[3] 페리스를 위한 사이트(!)도 존재한다.[4] Crustacean(갑각류)에서 따온 것으로 보인다.[5] Go개발자들이 스스로를 고퍼(Gopher)라고 하는 것과 유사하다.[6] 그레이던 호어는 이 뒤로도 한동안 수석 개발자로 개발에 참여하고 있다가, 2013년에 수석 개발자 지위를 내려놓은 상태이다.[7] Resource Acquisition Is Initialization의 약자로 C++에서 유래된 개념이다. 객체의 소멸자에 자원 수명을 묶어서 스코프를 벗어나면 자동으로 자원이 해제되도록 한다.[8] 일반적인 객체 지향 프로그래밍 언어와 비교해서 상당한 차이가 있다. Rust는 의도적으로 객체 상속을 지원하지 않도록 설계되었으며, 인터페이스 역할을 하는 트레이트를 하나의 객체에 여러 개 합성하는 시스템을 가지고 있다. 트레이트는 말 그대로 객체의 특성을 나타내며 상속이 가능하다. 예를 들자면
Clone
트레이트는 객체가 복제될 수 있음을 나타내는 동시에 복제 인터페이스를 제공한다.
[9] unsafe 스코프를 사용하지 않는다는 전제하에 dangling pointer, double-free, use-after-free 같은 메모리 버그를 작정하고 도입하려해도 아예 원천적으로 불가능하다. 단 러스트는 공식적으로 메모리 누수에 대해 보호를 보장하지는 않는다. 보다 정확히 말하자면 Rust는 모든 종류의 undefined behavior를 차단하는 것을 목표로 한다. 메모리 누수나 데드락과 같은 버그는 버그일지언정 늘 예측 가능한 동작을 하기에 undefined behavior는 아니다.[10] 정확히는 오버헤드가 큰 "추적 기반 쓰레기 수집" 을 사용하지 않는 것이고, Rc, Arc 등의 스마트 포인터에 "참조 기반 쓰레기 수집"이 포함되어 있다. Rust 및 C++로 작성되는 많은 프로그램은 스마트 포인터를 자주 사용하고 있다.[11] 1.32 이전에는 메모리 할당자인 jemalloc을 기본적으로 사용했기 때문에, 속도는 좀 더 빠르지만 OS 입장에서는 항상 사용하는 만큼만 메모리를 할당받는 것은 아니었다. 원하는 경우 라이브러리를 활용하거나 직접 구현해 메모리 할당자를 바꿀 수 있다.[12] 스레드 또한 시스템에서 직접 할당받아 사용한다. n:m 방식으로 스레드 할당을 받고자 하는 경우, 특정 라이브러리를 사용해야하며, 이렇게 하면 약간의 성능을 희생해 편의성을 얻을 수 있다.[13] 여기서는 스택 영역의 메모리에 존재하는 값을 뜻한다.[14] 스코프 내에서 할당이 이루어진 값은 스코프를 벗어나면 기본적으로 소멸자가 호출된다. 그러나 스코프 밖으로 소유권을 이전하는 경우 소멸자가 2번 호출돼서는 안되므로 기존 변수를 파기하게 된다.[15] 단, 후술되는 특수한 트레이트인
Copy
트레이트를 연산 되는 타입이 지니면 복사연산이 실행된다.
[16] 스코프의 제일 마지막에 삽입되는
return
은 생략할 수 있다.
[17] 값이 이동할 때 새로 메모리를 할당하고 메모리를 복사할 지, 그냥 주어진 메모리 영역을 그대로 쓸지 결정하는 것은 Rust 컴파일러의 몫으로, 최적화하기에 따라서는 함수로 인자를 넘기고 리턴받는 동안 단 한 번의 메모리 복사도 일어나지 않을 수도 있다. C++를 아주 잘 알고 있다면, RVO라는 약어가 익숙할 것이다.[18] Borrowed reference라고도 부른다[19] 그러나 Rust 컴파일러에서 수명과 관련한 오류를 확인하는 방식은 "책을 빌린 사람이 있으면 그 전에는 도서관 문을 닫지 마시오" 가 된다.[20]
RefCell
,
UnsafeCell
[21] C++ TS의
std::expected
와 비슷하다.
[22] 타입
T
U
의 합집합. 다른 언어에서는 주로
T | U
로 표기한다. TypeScript식 표기를 기준으로 '문자열 또는 숫자를 아이템으로 가지는 배열의 타입'은
Array<number | string>
와 같이 표시한다.
[23] Rust의 ABI는 안정적으로 제공되지 않으므로 이러한 내용은 언제든지 바뀔 수 있다.[24] 다른 언어를 기준으로 설명하자면, Java8 이후의 Java interface와 매우 비슷하다. 물론 차이는 있다.[25] C++20의 Concepts 기능과 유사한 역할을 한다.[26] 코루틴은 서브 루틴을 일시 정지하고 재개할 수 있는 구성 요소를 말한다. 쉽게 말해 필요에 따라 일시 정지할 수 있는 함수를 말하며, 이를 활용하여 I/O 처리를 극대화할 수 있다. 이는 단순히 대기해야 하는 작업이 처리되기 전에 다른 작업을 먼저 처리하도록 할 수 있기 때문이다. 멀티 스레드 환경에서도 공유 자원의 접근이 필요할 때 뮤텍스의 락을 얻지 못하는 경우, 다른 작업을 먼저 처리하도록 하여 컴퓨팅 자원의 사용을 극대화할 수 있다.[27] 단순 연산뿐만 아니라 네트워크, 파일 입출력, 타이머 등의 처리를 복합적으로 수행한다. 일반 Socket 을 사용할지, epoll 등의 event 기반 I/O 를 사용할지는 이 실행자 구현체의 세부 사항으로 결정된다.[28] 한글판[29] C++에서도 C++20 표준의 Concept을 통해서 Rust의 트레이트와 유사한 정보를 컴파일러에게 제공할 수 있게 됐다.[30] 심지어는 match(타언어의 switch)에서 default가 없는 등 빠진 논리 흐름이 있으면 컴파일이 안된다![31] 전에 컴파일했던 코드와 현재 컴파일하는 코드를 비교하여, 변경된 부분만 다시 컴파일하는 기법이다.[32] 컴파일 시에 일어나는 에러를 직접 컴파일 하지 않고 적은 비용으로 확인할 수 있다. 런타임시 발생 가능한 에러를 최대한 컴파일 타임에 잡아내는 러스트의 특성상 사용할 일이 많은 명령어이다.[33] 소스코드를 보기 좋게 자동으로 정렬해준다. [34] 컴파일 시에 일어나는 에러를 확인함과 동시에 쓸데없이 장황한 코드를 깔끔하게 만드는 방법을 제공하는 툴[35] 러스트의 MIR(Mid-Level IR)를 직접 독립된 환경에서 실행해주는 인터프리터. 일반적으로는 UB 검사 등에 사용한다.[36] LSP를 사용하기 때문에 비단 vscode뿐만이 아닌 neovim등 다른 에디터에서도 사용 가능하다.[37] 사실 2021년 초부터 리누스 토발즈가 리눅스 커널 개발에 러스트 도입을 가시화 하기는 했다.#[38] "we’re excited to announce that the Android Open Source Project (AOSP) now supports the Rust programming language for developing the OS itself." #[39] "our memory-safe language efforts are best focused on new development and not on rewriting mature C/C++ code." #[40] 2021년 10월, AOSP 페이지에 Rust 빌드 가이드 항목이 생겼다. https://source.android.com/setup/build/rust/building-rust-modules/overview?hl=en[41] Python이 현재의 인기에 도달하기까지 30년이 걸렸다[42] 종이책은 절판됐고 전자책은 구매가능하지만, 개정판 번역이 예정되어있다.[43] the book에는 없는 비동기와 관련된 고급 내용들이 많이 들어있다.[44] 디스코드 서버가 아래 서버와 함께 상당히 활동적이다. 예의만 잘 지킨다면 초보자들은 질문이 생겼을 때 여기서 상당히 친절한 답변을 받을 수 있다.[45] 원래 공식 서버가 아니었으나, 정식 파트너쉽을 맺었다.


파일:크리에이티브 커먼즈 라이선스__CC.png 이 문서의 내용 중 전체 또는 일부는 2023-12-31 00:48:55에 나무위키 Rust(프로그래밍 언어) 문서에서 가져왔습니다.