문서화된 동작만에 의존해 stable Rust에서 특수화를 관찰할 수 있는지 살펴봅니다.
max는 누구인가요?atom 피드이 사이트는 JavaScript 없이 보는 것이 가장 좋습니다
(2026년 4월 28일 게시) Rust 유지관리자들에게 사과하며
Rust 표준 라이브러리가 내부적으로 특수화를 사용한다는 것은 잘 알려진 사실입니다. 지난 몇 년 동안 사용자 코드에서 이 동작을 관찰하여 stable에서 대체 특수화를 달성하려는 여러 시도가 있었습니다. 예를 들어, 배열에서 제거된 Clone 특수화는 유명한 라이브러리에서 이런 방식으로 사용되었습니다.
이 꼼수의 문제는 특수화가 보장되지 않기 때문에 Rust 업데이트를 거치며 깨질 수 있다는 점입니다. 실제로 저 경우가 그랬습니다. 하지만 예외가 하나 있습니다.
계속하기 전에, 이제부터 제가 말하려는 모든 내용에도 불구하고, 저는 이 꼼수를 진지한 코드에서 실제로 사용하는 것을 권장하지 않는다는 점을 밝히고 싶습니다.
그렇다고는 하지만...
이터레이터에는 .fuse()라는 메서드가 있습니다. 이 메서드는 이터레이터가 값을 모두 소진한 뒤에는 항상 None을 반환하고, 패닉을 일으키거나, 갑자기 다시 Some(_)을 반환하거나, 그 밖의 이상한 동작을 하지 않도록 보장합니다.
최적화로서, 원래 이터레이터가 FusedIterator를 구현했다면 .fuse()는 비용이 들지 않으며 다른 로직 없이 항상 호출을 그대로 위임합니다. 특히, 이것은 Iterator::fuse() 문서에서 보장 됩니다:
Fuse래퍼는FusedIterator트레이트를 구현하는 이터레이터에서는 아무 일도 하지 않습니다.
그리고 FusedIterator 문서에서도 마찬가지입니다:
이터레이터가 이미 fused 상태라면, 추가적인
Fuse래퍼는 성능 저하 없이 아무 일도 하지 않습니다.
제가 아는 한, 이것이 보장된 특수화가 실제로 발생하는 유일한 경우입니다. 그리고 저는 실제로 그것들을 전부 훑어보며 확인했습니다.
따라서 저는 문서화된 동작에만 의존하여 stable Rust에서 특수화를 관찰하는 것이 가능하다고 생각합니다. 예시로 어떤 타입이 Send를 구현하는지 검사하는 함수를 사용해 봅시다:
rust (playground)
pub fn is_send<T: ?Sized>() -> bool {
struct Checker<'a, T: ?Sized>(&'a mut u8, PhantomData<T>);
impl<T: ?Sized> Iterator for Checker<'_, T> {
type Item = ();
fn next(&mut self) -> Option<()> {
// 우리가 몇 번 호출되었는지 센다
*self.0 += 1;
// 이터레이터는 항상 `None`을 반환하므로, `Fuse`는
// 첫 호출 뒤에는 더 이상 이것을 호출하지 않아야 한다...
None
}
}
// ...이미 fused 상태가 아니라면 말이다. 그리고 그것은 `T: Send`일 때 참이다
impl<T: ?Sized + Send> FusedIterator for Checker<'_, T> {}
let mut counter = 0;
let mut it = Checker(&mut counter, PhantomData::<T>).fuse();
// `.fuse()`가 아무 일도 하지 않았는지 확인하기 위해 이터레이터를 두 번 진행시킨다
it.next();
it.next();
// 우리가 두 번 호출되었다면, `.fuse()`가 두 번째 호출을 위임한 것이므로
// `T`는 `Send`를 구현해야 한다
counter == 2
}
assert!(is_send::<u8>());
assert!(!is_send::<*const u8>());
안타깝게도, 제 생각에는 이것은 const 컨텍스트에서는 사용할 수 없어서 generic_const_exprs가 있더라도 이를 타입 수준으로 끌어올릴 수 없습니다. 다만 이것은 generic_const_exprs와 const 트레이트를 함께 취하면 사실상 특수화를 허용하게 된다는 뜻일 수는 있습니다.