[Rust] 1.87.0 업데이트와 Rust 10주년 (번역)

https://blog.rust-lang.org/2025/05/15/Rust-1.87.0/
네덜란드 Utrecht에서 열린 Rust 10주년 축하 행사와 함께, Rust 버전 1.87를 발표하게되어 기쁩니다!

게다가 오늘은 러스트 1.0 릴리즈로부터 딱 10주년이에요!

과거와 현재에 걸쳐 Rust를 작업해 주신 수많은 기여자들께 감사드립니다. 여기 N0년어치의 Rust가 있습니다! 🎉

여느 때와 마찬가지로 이번 버전에는 러스트 1.0 이후 우리가 따랐던 릴리즈 사이클대로, 6주 동안 베타 버전의 일부였던 모든 변경 사항이 포함되어 있습니다.

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

rustup update stable

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

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

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



1.87 Stable에는 무엇이 있나요?

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

Anonymous pipes

1.87은 표준 라이브러리에 anonymous pipe에 대한 액세스를 추가합니다.
여기에는 std::process::Command의 입출력 메서드와의 통합을 포함합니다.

예를 들어, stdout와 stderr 스트림들을 하나로 Join하는 것은 이제 아래와 같이 비교적 간단해집니다. 예전에는 추가 스레드나 플랫폼별 함수가 필요했어요.

use std::process::Command;
use std::io::Read;


let (mut recv, send) = std::io::pipe()?;


let mut command = Command::new("path/to/bin")
** **** // Both stdout and stderr will write to the same pipe, combining the two.******
** .stdout(send.try_clone()?)**
** .stderr(send)**
** .spawn()?;**


let mut output = Vec::new();
recv.read_to_end(&mut output)?;


// It's important that we read from the pipe before the process exits, to avoid
// filling the OS buffers if the program emits too much output.****
assert!(command.wait()?.success());




Safe architecture intrinsics

target feature의 enable을 필요로 해서 unsafe했던 대부분의 std::arch intrinsic은 이제 해당 feature를 enable한 safe code 내에서 호출 가능합니다.

원래는 manual intrinsic을 사용하여 배열의 합산을 구현한다면, 이렇게만 할 수 있었습니다.

#![forbid(unsafe_op_in_unsafe_fn)]
use std::arch::x86_64::*;


fn sum(slice: &[u32]) -> u32 {
** #[cfg(target_arch = "x86_64")]**
** {**
** if is_x86_feature_detected!("avx2") {**
** **** // SAFETY: We have detected the feature is enabled at runtime,**
** // so it's safe to call this function.******
** return unsafe { sum_avx2(slice) };**
** }**
** }**


** slice.iter().sum()**
}


원래는 unsafe를 써야했지만, 이제는 코어 루프에 safe 코드를 사용할 수 있습니다. 이렇게요.****


#[target_feature(enable = "avx2")]
#[cfg(target_arch = "x86_64")]
fn sum_avx2(slice: &[u32]) -> u32 {
** **** // SAFETY: __m256i and u32 have the same validity.******
** let (prefix, middle, tail) = unsafe { slice.align_to::<__m256i>() };**


** let mut sum = prefix.iter().sum::();**
** sum += tail.iter().sum::();**


** **** // Core loop is now fully safe code in 1.87, because the intrinsics require**
** // matching target features (avx2) to the function definition.******
** let mut base = _mm256_setzero_si256();**
** for e in middle.iter() {**
** base = _mm256_add_epi32(base, e);*
** }**


** // SAFETY: __m256i and u32 have the same validity.**
** let base: [u32; 8] = unsafe { std::mem::transmute(base) };**
** sum += base.iter().sum::();**


** sum**
}




asm!에서 Rust code로 점프

Inline assembly(asm!)은 이제 러스트 코드 내에서 레이블이 지정된 블록으로 점프할 수 있습니다.

이는 보다 유연한 로우레벨 프로그래밍을 가능하게 합니다. 예를 들면, OS 커널에서 최적화된 제어 흐름을 구현하거나 하드웨어와 보다 효율적으로 상호작용하는 것들 말이죠.

asm! macro는 이제 label 피연산자를 제공합니다. jump target용으로요.
그리고 이 label은 반드시 ()나 !를 반환타입으로 갖는 블록 표현식이어야 합니다.

이 블록은 점프될때 실행되고, 그 실행은 asm! 블록 이후로 이어집니다. 출력과 label 피연산자를 같은 asm! 호출에서 쓰는 것은 아직 unstable합니다.

unsafe {
** asm!(**
** "jmp {}",**
** label {**
** println!("Jumped from asm!");**
** }**
** );**
}

자세한 내용은 reference를 참조해주세요.




trait 정의 내 impl Trait에서의 Precise capturing (+ use<...>)

이 릴리즈는 impl Trait 리턴타입을 사용해서 trait 정의에서 캡처된 제너릭타입과 라이프타임을 지정하는 것을 안정화합니다.

이를 통해 trait 정의에 이 기능을 사용할 수 있으며, 1.82에서 진행했던 non-trait 기능에 대한 안정화를 확장할 수 있습니다.

다음은 설명을 위한 desugaring 예시 코드입니다.

trait Foo {
** fn method<'a>(&'a self) -> impl Sized;**


** // ... desugars to something like:**
** type Implicit1<'a>: Sized;**
** fn method_desugared<'a>(&'a self) -> Self::Implicit1<'a>;**


** // ... whereas with precise capturing ...**
** fn precise<'a>(&'a self) -> impl Sized + use;**


** **** // ... desugars to something like:******
** type Implicit2: Sized;**
** fn precise_desugared<'a>(&'a self) -> Self::Implicit2;**
}




Stable이 된 API

Vec::extract_if
vec::ExtractIf
LinkedList::extract_if
linked_list::ExtractIf
<[T]>::split_off
<[T]>::split_off_mut
<[T]>::split_off_first
<[T]>::split_off_first_mut
<[T]>::split_off_last
<[T]>::split_off_last_mut
String::extend_from_within
os_str::Display
OsString::display
OsStr::display
io::pipe
io::PipeReader
io::PipeWriter
impl From for OwnedHandle
impl From for OwnedHandle
impl From for Stdio
impl From for Stdio
impl From for OwnedFd
impl From for OwnedFd
Box<MaybeUninit>::write
impl TryFrom<Vec> for String
<*const T>::offset_from_unsigned
<*const T>::byte_offset_from_unsigned
<*mut T>::offset_from_unsigned
<*mut T>::byte_offset_from_unsigned
NonNull::offset_from_unsigned
NonNull::byte_offset_from_unsigned
::cast_signed
NonZero::::cast_signed.
::cast_unsigned.
NonZero::::cast_unsigned.
::is_multiple_of
::unbounded_shl
::unbounded_shr
::unbounded_shl
::unbounded_shr
::midpoint
::from_utf8
::from_utf8_mut
::from_utf8_unchecked
::from_utf8_unchecked_mut

다음 stable API들은 이제 const context에서도 stable입니다.

core::str::from_utf8_mut
<[T]>::copy_from_slice
SocketAddr::set_ip
SocketAddr::set_port,
SocketAddrV4::set_ip
SocketAddrV4::set_port,
SocketAddrV6::set_ip
SocketAddrV6::set_port
SocketAddrV6::set_flowinfo
SocketAddrV6::set_scope_id
char::is_digit
char::is_whitespace
<[[T; N]]>::as_flattened
<[[T; N]]>::as_flattened_mut
String::into_bytes
String::as_str
String::capacity
String::as_bytes
String::len
String::is_empty
String::as_mut_str
String::as_mut_vec
Vec::as_ptr
Vec::as_slice
Vec::capacity
Vec::len
Vec::is_empty
Vec::as_mut_slice
Vec::as_mut_ptr




i586-pc-windows-msvc target 제거

Tier 2 타겟 i586-pc-windows-msvc이 제거됩니다.

i586-pc-windows-msvc가 훨씬 더 인기 있는 Tier 1 타겟 i686-pc-windows-msvc와 다른 점은, i586-pc-windows-msvc가 SSE2 명령어 지원을 요구하지 않는다는 겁니다.

그런데 Windows 10와 모든 windows 타겟의 최소 요구 OS 버전이 이제 모두 SSE2 인스트럭션을 요구합니다. (Windows 7 및 그 이하 유물을 제외하고요)

현재 i586-pc-windows-msvc를 타겟으로 하는 모든 사용자는 i686-pc-windows-msvc로 마이그레이션해야 합니다.

자세한 내용은 Major Change Proposal에서 확인하실 수 있습니다.




기타 변경점

이외의 모든 변경사항은 각각 Rust, Cargo, Clippy에서 확인하세요.




1.87.0의 컨트리뷰터들에게

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

고마워요!