[Rust] 1.66.0 업데이트 발표 (번역)

[원본 링크]

https://blog.rust-lang.org/2022/12/15/Rust-1.66.0.html

우리 러스트 팀은 새 버전 [1.66.0]을 발표하게 돼서 정말 기쁩니다!
러스트는 누구든 믿음직하고 효과적인 소프트웨어를 만들 수 있게 도와주는 끝내주는 언어입니다.

만약 rustup을 통해서 Rust의 이전버전을 설치해놓은 상대라면, 업데이트는 아주 쉽습니다. 그냥 이렇게 치면 돼요.
rustup update stable

rustup을 설치한 적이 없다면, 우리 웹사이트의 설치 페이지에서 받을 수 있습니다. 그리고 깃허브에서 이번 버전에 대한 릴리즈 노트를 참조해보세요.

미래의 릴리즈를 테스트해서 러스트 팀을 돕고 싶다면, 로컬에서 베타 채널(rustup default beta) 또는 nightly 채널(rustup default nightly)로 업데이트하는 것을 고려할 수 있습니다.

버그를 발견했다면 리포트해주세요!




1.66.0 stable에는 무엇이 있나요?


필드가 있는 enum에 대한 명시적 판별(discriminants)

정수 표현(repr)이 달린 열거형은 이제 필드가 있는 경우에도 명시적 판별 값을 사용할 수 있습니다.

**#[repr(u8)] **
**enum Foo { **
** A(u8), **
** B(i8), **
** C(bool) = 42, **
**} **

이전에도 repr이 달린 enum에 명시적 판별 값을 사용할 수는 있었지만, 해당 variant들에 필드가 없는 경우에만 가능했습니다.

enum의 표현(repr)이 두 언어에서 일치해야 하는 언어 경계를 넘어 값을 전달할 때 명시적 판별자가 유용합니다.
명시적 판별 값은 다른 언어 사이에서 enum 값을 전달할 때 유용합니다.

예를 들면, 이런거죠.

**#[repr(u8)] **
**enum Bar { **
** A, **
** B, **
** C = 42, **
** D, **
}

여기서 Bar enum은 u8과 동일한 레이아웃을 갖는 것이 보장됩니다. 그리고 Bar::C variant는 판별값 42를 갖죠.

명시적으로 지정된 값이 없는 variant에는 소스 코드의 순서에 따라 자동으로 할당되는 판별값이 있습니다.
그래서 저기서 Bar::A는 0, Bar::B는 1, Bar::D는 43입니다.

이 기능을 쓰지 않고 Bar::C의 명시적 값을 설정하려면 그 앞에 41개의 불필요한 variant을 추가할 수밖에 없었어요..!

참고로 필드가 없는 enum의 경우 as 캐스팅을 통해 판별자를 검사할 수 있는데요. (예: Bar::C as u8)

Rust는 아직 필드가 있는 enum의 raw 판별값에 액세스하는 언어 수준의 방법을 제공하지 않습니다.
대신 현재 unsafe 코드를 사용하여 필드가 있는 enum의 판별값을 검사해야 합니다.

이 기능은 unsafe 코드를 필요한 언어간 FFI와 함께 사용하기 위한 것이니 추가적인 부담이 되지 않기를 바랍니다.

만약 판별값에 대한 불투명한 핸들이 필요하다면 std::mem::discriminant 함수를 참조하세요.




core::hint::black_box

컴파일러에서 생성된 머신코드를 벤치마킹하거나 검사할 때면은, 특정 위치에서 최적화가 발생하지 않도록 방지하는 것이 가끔은 유용합니다.

다음 예에서 push_cap 함수는 Vec::push를 루프로 4번 실행합니다.

**fn push_cap(v: &mut Vec) { **
** for i in 0..4 { **
** v.push(i); **
** } **
**} **

**pub fn bench_push() -> Duration { **
** let mut v = Vec::with_capacity(4); **
** let now = Instant::now(); **
** push_cap(&mut v); **
** now.elapsed() **
**} **

x86_64에서 컴파일러의 최적화된 출력을 검사하면 코드가 다소 짧게 보일 겁니다.

**example::bench_push: **
**sub rsp, 24 **
**call qword ptr [rip + std::time::Instant::now@GOTPCREL] **
**lea rdi, [rsp + 8] **
**mov qword ptr [rsp + 8], rax **
**mov dword ptr [rsp + 16], edx **
**call qword ptr [rip + std::time::Instant::elapsed@GOTPCREL] **
**add rsp, 24 **
**ret **

사실은, 우리가 벤치마킹하고 싶었던 push_cap 함수 전체가 최적화된거에요!

새로 안정화된 black_box 기능을 사용하면 이 문제를 해결할 수 있습니다.
기능적으로 black_box는 그다지 신기한건 아닙니다. 값을 그냥 바로 다시 전달할 뿐이거든요.

하지만 컴파일러는 black_box를 보면 입력으로 무엇이든 받을 수 있고 모든 값을 반환하는 함수로 취급합니다.

이것은 위에서 발생한 최적화를 비활성화하는 데 매우 유용합니다.
예를 들어 for 루프가 반복될 때마다 벡터가 실제로 무언가에 사용될 것임을 컴파일러에 암시할 수 있습니다.

**use std::hint::black_box; **
**fn push_cap(v: &mut Vec) { **
** for i in 0..4 { **
** v.push(i); **
** black_box(v.as_ptr()); **
** } **
**} **

이제 최적화된 어셈블리 출력에서 unroll된 for 루프를 찾을 수 있습니다.

**mov dword ptr [rbx], 0 **
**mov qword ptr [rsp + 8], rbx **
**mov dword ptr [rbx + 4], 1 **
**mov qword ptr [rsp + 8], rbx **
**mov dword ptr [rbx + 8], 2 **
**mov qword ptr [rsp + 8], rbx **
**mov dword ptr [rbx + 12], 3 **
**mov qword ptr [rsp + 8], rbx **

이 어셈블리 출력에서는 black_box를 호출의 사이드이펙트를 볼 수 있습니다.
mov qword ptr [rsp + 8], rbx 명령은 계속 쓸데없이 반복되죠. 이 인스트럭션은 실제로는 호출되지 않는 함수의 첫 번째 인수로 주소 v.as_ptr()를 씁니다.

생성된 코드는 push call에 의해 introduce된 할당의 가능성과 전혀 관련이 없습니다.
이는 컴파일러가 여전히 bench_push 함수에서 Vec::with_capacity(4)를 호출했다는 사실을 사용하고 있기 때문입니다.

black_box의 배치를 가지고 놀거나 여러 위치에서 사용하면 컴파일러 최적화에 미치는 영향을 확인할 수 있습니다.




cargo remove

Rust 1.62.0에서는 프로젝트에 종속성을 추가하는 명령행 도구인 cargo add를 공식 도입했었죠.

이제는 cargo remove를 통해 반대로 종속성을 제거할 수도 있습니다!




Stable이 된 API

다음 요소들은 이제 stable입니다.

proc_macro::Span::source_text

u*::{checked_add_signed, overflowing_add_signed, saturating_add_signed, wrapping_add_signed}

i*::{checked_add_unsigned, overflowing_add_unsigned, saturating_add_unsigned, wrapping_add_unsigned}

i*::{checked_sub_unsigned, overflowing_sub_unsigned, saturating_sub_unsigned, wrapping_sub_unsigned}

BTreeSet::{first, last, pop_first, pop_last}
BTreeMap::{first_key_value, last_key_value, first_entry, last_entry, pop_first, pop_last}

WASI에서 stdio lock 타입에 대한 AsFd 구현 추가

impl TryFrom<Vec> for Box<[T; N]>
core::hint::black_box
Duration::try_from_secs_{f32,f64}
Option::unzip
std::os::fd




기타 변경점

Rust 1.66 릴리스에는 다음과 같은 기타 변경 사항이 있습니다.

  1. 이제 패턴에서 ..=x 범위를 사용할 수 있습니다.
    2 Linux 빌드는 이제 LTO 및 BOLT를 사용하여 rustc 프론트엔드 및 LLVM 백엔드를 각각 최적화, 런타임 성능과 메모리 사용량을 모두 개선합니다.

그리고 Rust, Cargo, Clippy에서 무엇이 바뀌었는지 확인해보세요.




1.66.0의 컨트리뷰터들

1.66.0의 완성엔 수많은 사람들이 함께했습니다. 전부 여러분이 없었다면 불가능했을 거에요.
고마워요!



Search