[Rust] 1.78.0 업데이트 발표 (번역)
https://blog.rust-lang.org/2024/05/02/Rust-1.78.0.html
러스트는 누구든 믿음직하고 효과적인 소프트웨어를 만들 수 있게 도와주는 끝내주는 언어입니다.
만약 rustup을 통해서 Rust의 이전버전을 설치해놓은 상대라면, 업데이트는 아주 쉽습니다. 그냥 이렇게 치면 돼요.
rustup update stable
rustup을 설치한 적이 없다면, 우리 웹사이트의 설치 페이지에서 받을 수 있습니다. 그리고 깃허브에서 이번 버전에 대한 릴리즈 노트를 참조해보세요.
미래의 릴리즈를 테스트해서 러스트 팀을 돕고 싶다면, 로컬에서 베타 채널(rustup default beta) 또는 nightly 채널(rustup default nightly)로 업데이트하는 것을 고려할 수 있습니다.
버그를 발견했다면 리포트해주세요!
1.78.0 stable에는 무엇이 있나요?
Diagnostic attributes
이제 Rust는 컴파일러 오류 메시지에 영향을 주기 위한 용도의 #[diagnostic] attribute namespace를 지원합니다.
이건 컴파일러가 사용할 필요 없는 힌트로 처리되며, 컴파일러가 인식하지 못하는 진단을 제공하는 것도 오류는 아닙니다.
이러한 유연성을 통해 소스 코드의 버전이 다르거나, 완전히 다른 구현일때도 상관없이 모든 컴파일러에서 지원되지 않는 경우에도 진단을 제공할 수 있습니다.
이 네임스페이스의 첫번째 attribute는 #[diagnostic::on_unimplemented]입니다.
이건 trait이 필요하지만 타입에 구현되지 않은 경우 메시지를 사용자 정의하기 위해 특성에 배치할 수 있어요.
안정화 pull request에 제공된 예시를 참고해보세요.
#[diagnostic::on_unimplemented(
** message = "My Message for ImportantTrait<{A}> is not implemented for {Self}",**
** label = "My Label",**
** note = "Note 1",**
** note = "Note 2"**
)]
trait ImportantTrait {}
fn use_my_trait(_: impl ImportantTrait
fn main() {
** use_my_trait(String::new());**
}
이전에는 컴파일러가 다음과 같은 builtin 오류를 표시했습니다.
error[E0277]: the trait bound String: ImportantTrait<i32> is not satisfied
--> src/main.rs:12:18
|
12 | use_my_trait(String::new());
| ------------ ^^^^^^^^^^^^^ the trait ImportantTrait<i32> is not implemented for String
| |
| required by a bound introduced by this call
|
#[diagnostic::on_unimplemented]를 사용하면 커스텀 메시지가 기본 오류 라인을 채우고 해당 커스텀 레이블이 소스 출력에 배치됩니다.
원본 레이블은 여전히 도움말 출력으로 기록되며 모든 커스텀 메모도 기록됩니다.
단, 이에 대한 정확한 세부 사항은 변경될 수 있습니다.
error[E0277]: My Message for ImportantTrait<i32> is not implemented for String
--> src/main.rs:12:18
|
12 | use_my_trait(String::new());
| ------------ ^^^^^^^^^^^^^ My Label
| |
| required by a bound introduced by this call
|
= help: the trait ImportantTrait<i32> is not implemented for String
= note: Note 1
= note: Note 2
trait 작성자가 누락된 trait impl 자체에 대해 그냥 이야기하는 것보다 더 나은 힌트를 제공할 수 있는 경우, 이러한 종류의 진단이 더욱 유용합니다.
예를 들어, 다음은 표준 라이브러리의 요약된 샘플입니다.
#[diagnostic::on_unimplemented(
** message = "the size for values of type {Self} cannot be known at compilation time",**
** label = "doesn't have a size known at compile-time"**
)]
pub trait Sized {}
자세한 내용은 진단 도구 attribute 네임스페이스에 대한 참조 섹션을 참고하세요.
Asserting unsafe preconditions
Rust 표준 라이브러리에는 unsafe 함수의 전제 조건(preconditions)에 대한 여러 assertion이 있었지만, 역사적으로 release 퍼포먼스에 영향을 주지 않기 위해 표준 라이브러리의 #[cfg(debug_assertions)] 빌드에서만 활성화되었습니다.
그러나 표준 라이브러리는 일반적으로 release 모드에서 컴파일되고 배포되므로 대부분의 Rust 개발자는 이러한 검사를 전혀 실행하지 않았습니다.
이제 이러한 assertion의 조건은 코드 생성까지 지연되므로 사용자의 디버그 assertion 설정에 따라 확인됩니다. 디버그 및 테스트 빌드에서는 기본적으로 활성화됩니다.
이 변경은 사용자가 코드에서 undefined behavior를 포착하는 데는 도움이 되지만, 확인되는 정도에 대한 세부 사항은 일반적으로 stable하지 않습니다.
예를 들어, Slice::from_raw_parts에는 non-null이면서 align된 포인터가 필요합니다. 의도적으로 잘못 align된 포인터를 다음과 같이 사용하면 undefined behavior가 발생합니다.
운이 좋지 않았다면 과거에는 "동작"하는 것처럼 보였을 수 있지만, 이제 디버그 assertion에서 이를 포착할 수 있습니다.
fn main() {
** let slice: &[u8] = &[1, 2, 3, 4, 5];**
** let ptr = slice.as_ptr();**
** **** // u16의 올바른 alignment에서 항상 벗어나는 ptr에서 오프셋을 만듭니다. ******
** let i = usize::from(ptr as usize & 1 == 0);**
** let slice16: &[u16] = unsafe { std::slice::from_raw_parts(ptr.add(i).cast::
** dbg!(slice16);**
}
thread 'main' panicked at library/core/src/panicking.rs:220:5:
unsafe precondition(s) violated: slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed isize::MAX
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace
thread caused non-unwinding panic. aborting.
Deterministic realignment
표준 라이브러리에는 포인터와 슬라이스의 alignment를 변경하는 몇 가지 함수들이 있지만, 이전에는 문서를 정확하게 따랐을 경우 실제로 의지하기 어렵게 만드는 주의사항이 있었습니다.
이러한 경고는 주로 const 평가에 대한 대비책으로 존재했지만 어쨌든 non-const 사용에만 안정적입니다. 이제는 실제 입력에 따라 일관된 런타임 동작이 보장됩니다.
pointer::align_offset은 주어진 alignment에 대한 포인터를 변경하는 데 필요한 오프셋을 계산합니다. 가능하지 않은 경우에는 항상 usize::MAX를 반환합니다.
이전에는 항상 usize::MAX를 반환하는 것이 허용되었으나 이제 해당 동작이 제거되었습니다.
slice::align_to 및 slice::align_to_mut는 둘 다 슬라이스를 align된 중간 슬라이스와 align되지 않은 나머지 head 및 tail 슬라이스로 변환합니다.
이러한 메서드는 이제 모든 것을 head 슬라이스로 반환하는 것과 같이 덜 최적인 것을 반환하도록 허용하는 대신 가능한 가장 큰 중간 부분을 반환할 것을 약속합니다.
안정화된 API
impl Read for &Stdin
Accept non 'static lifetimes for several std::error::Error related implementations
Make impl<Fd: AsFd> impl take ?Sized
impl From
다음 API들은 이제 const context에서도 stable입니다.
Barrier::new()
호환성 노트
이전에 발표한대로 Rust 1.78은 아래 target들에 대한 최소 요구 사항을 Windows 10으로 늘렸습니다.
- x86_64-pc-windows-msvc
- i686-pc-windows-msvc
- x86_64-pc-windows-gnu
- i686-pc-windows-gnu
- x86_64-pc-windows-gnullvm
- i686-pc-windows-gnullvm
Rust 1.78은 번들로 제공되는 LLVM을 버전 18로 업그레이드하여 x86-32/x86-64 타겟에 대해 발표된 u128/ i128 ABI 변경을 완료했습니다. 18보다 오래된 자체 LLVM을 사용하는 배포자는 여전히 해당 포스트에 언급된 calling convention 버그에 직면할 수 있습니다.
기타 변경점
이외의 모든 변경사항은 각각 Rust, Cargo, Clippy에서 확인하세요.
1.78.0의 컨트리뷰터들에게
1.78.0의 완성엔 수많은 사람들이 함께했습니다. 전부 여러분이 없었다면 불가능했을 거에요.