불안정한 할당자 트레이트와 현재 우리가 서 있는 지점에 대한 정리
불안정한 할당자 트레이트와 현재 우리가 서 있는 지점에 대한 살펴보기
rust 2026-03-11 Allocators RFC는 채택된 지 거의 10년이라는 이정표에 다가가고 있지만, 안정화는 여전히 이뤄지지 않았습니다.
2025 State of Rust Survey에서 안정화로 인해 막힌 일이 풀릴 것이라고 답한 비율이 ~12%, 코드가 개선될 것이라고 답한 비율이 추가로 **~27%**에 달하는 것을 보면, 여전히 상당수의 Rust 사용자들이 원하는 기능처럼 보입니다.
이제는 현황을 정리해 볼 때가 됐다고 생각했습니다. 현재 할당자가 어떤 상태인지, 무엇이 막고 있는지, 그리고 남아 있는 치명적 이슈가 무엇인지 파악해 보려 합니다. 이 글이 약간의 활동을 촉발하고, 오래된 RFC 중 하나에 새 숨을 불어넣기를 바랍니다.
할당자 트레이트는 nightly에서 구현되어 있지만, 여전히 불안정(unstable)합니다. 트레이트로 정의된 지 6년 이상 되었고, 트레이트의 핵심 메서드는 크게 변하지 않았습니다.
현재 트레이트 구현에는 간단한 필수 메서드 두 개가 있습니다:
pub unsafe trait Allocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
// ...other provided methods...
}
이 트레이트를 구현하면 Box나 Vec 같은 std 컨테이너 타입과 함께 사용하여, 커스텀 할당자가 할당한 값들을 반환할 수 있습니다:
let mut vec: Vec<i32, MyAllocator> = Vec::new_in(MyAllocator);
이제 Vec 컨테이너는 Vec<T, A>처럼 두 개의 제네릭 타입을 갖습니다. 컨테이너가 담는 타입 T, 그리고 할당자 타입 A입니다(사실 더 깊이 파고들면 Vec<T>는 전역 할당자를 쓰는 특수한 경우일 뿐이라는 것을 볼 수 있습니다. 즉 Vec<T, A = Global>입니다).
Vec 안에 할당자를 보관해야 하는 이유는, 크기 조정(resize), 자르기(truncate), 혹은 메모리 해제(drop)를 하고 싶을 때 반드시 같은 할당자를 사용해야 하기 때문입니다. 그렇지 않으면 다른 할당자의 메모리를 건드리게 되어 Undefined Behaviour를 피하기가 거의 불가능합니다.
실제로 Global 같은 Zero Sized Type(ZST)을 쓰는 경우에는 이 필드는 인라인 컴파일 과정에서 사라져서 vec는 표준적인 ptr, len, capacity만 남습니다. 하지만 상태를 필요로 하는 다른 할당자에서는 Vec의 크기가 할당자 필드를 포함하도록 커집니다.
Box<T>도 마찬가지로 기본적으로는 Box<T, A>이고, 기본값은 Box<T, A = Global>입니다. Box는 메모리 할당/변경 측면에서 훨씬 단순합니다. 생성될 때만 실질적으로 할당되고, 리사이즈나 트렁케이트 같은 일은 없지만, 그래도 해제(deallocate)를 위해 할당자를 보관해야 합니다.
이렇게 단순한 트레이트 정의라면 합의가 쉬울 것 같지 않나요? 메서드 두 개뿐이고, 이를 지원하기 위한 대부분의 힘든 작업도 nightly에 이미 들어가 있습니다.
하지만: 이렇게 단순한 정의조차도 여전히 답이 나지 않은 질문들이 있습니다.
이 글을 쓰는 시점에서 Rust allocator working group 저장소에는 열려 있는 이슈가 75개 있습니다. 로드맵도 오래 손대지 않은 상태이고, 아마 어느 정도 정리가 필요해 보입니다.
Thom Chiovoloni는 몇 년 전 할당자 트레이트와 존재하는 몇 가지 블로커에 대해 글을 썼는데, 지금도 그때 언급된 많은 이슈들이 닫히지 않았습니다. 그 이후에도 reddit의 많은토론과 다른 곳들에서의 논의가 계속되었습니다.
2026년 초 현재, 여전히 합의에 도달하지 못한 것으로 보입니다. 기존 이슈들 중 다수가 남아 있는 가운데, 여기서는 그중에서도 주요한 것들에 대한 제 의견을 적어봅니다. 목록은 포괄적이지 않지만, 지금까지 트레이트가 안고 있는 더 “큰” 스타일의 문제를 몇 가지 다룬다고 생각합니다.
분명히 해두자면: 저는 아래 변경들 중 어느 하나의 포함을 강하게 주장하는 것은 아닙니다. 제 바람은 약간의 토론을 촉발하는 것입니다.
현재 트레이트는 Layout이 zero sized가 되는 것을 허용합니다. 2020년에도 이것이 zero sized type에서 문제를 일으킨다는 것이 알려져 있었습니다. 문제는 할당자 계약(contract)이 zero sized 할당에 대해 같은 포인터를 허용한다는 점입니다. 실제로는 “아무것도 할당하지” 않기 때문이죠. 그러면 서로 다른 것들에 대해 같은 포인터를 반환하는 것이 Undefined Behaviour로 간주될 수도 있습니다(엄밀히 그렇다고 확신하진 못합니다). 게다가 포인터 동등성(pointer equality)도 깨집니다.
즉, 모든 할당자 구현에서 이 경우를 특수 처리해야 합니다. 예를 들어:
impl Allocator for MyAllocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
if layout.size() == 0 {
return Ok(NonNull::dangling());
}
// do actual allocations here
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
if layout.size() == 0 {
return;
}
// do actual deallocations here
}
}
따라서 할당자의 모든 구현이 이 예제와 비슷한 코드를 필요로 하거나, 최소한 zero sized 할당을 고려해야 하거나, 계약을 지키기 위해 쓸데없이 공간을 할당해야 합니다. 그렇다면 Layout을 지금처럼 유지하는 게 타당할까요?
가능한 해결책 중 하나는, 할당이 실제로 크기를 가지도록 강제하는 NonZeroLayout 타입을 도입하는 것입니다. 이렇게 하면 zero sized 할당과 관련된 몇몇 위험 요소(footgun)를 피할 수 있지만, 트레이트 소비자에게는 복잡성이 늘어납니다.
이는 트레이트 인터페이스의 작은 변경입니다:
/// Our new non zero layout (other constraints left out for brevity)
pub struct NonZeroLayout {
size: NonZeroUsize,
align: NonZeroUsize,
}
pub unsafe trait Allocator {
fn allocate(&self, layout: NonZeroLayout) -> Result<NonNull<[u8]>, AllocError>;
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: NonZeroLayout);
}
덧붙임: C의 malloc 동작을 보면, null 포인터를 반환하는 것이 허용됩니다(그 외도 여러 가지). 하지만 Rust의 allocate 반환은 항상 NonNull이기 때문에, NonZeroLayout을 강제하는 것이 이런 인자 타입들을 꽤 균형 있게 만들어 준다고 생각합니다.
막힌 것을 풀기 위해서(그리고 아마도 다른 사용 사례이기 때문입니다. 커널에서는 패닉이 더 심각한 일이니까요), rust for linux 팀은 할당자 안정화 없이도 일을 진행해 왔습니다. 그들이 만든 형태를 살펴볼 가치가 있는데, 왜냐하면 현재 형태 그대로 트레이트가 안정화되더라도 그들의 사용 사례에는 충분하지 않을 것처럼 느껴지기 때문입니다.
그들의 allocator trait는 현재 기존 트레이트와 약간 비슷하지만, 몇 가지 인자를 더 도입합니다:
pub unsafe trait Allocator {
const MIN_ALIGN: usize;
// Required method
unsafe fn realloc(
ptr: Option<NonNull<u8>>,
layout: Layout,
old_layout: Layout,
flags: Flags,
nid: NumaNode,
) -> Result<NonNull<[u8]>, AllocError>;
// Provided methods
fn alloc(
layout: Layout,
flags: Flags,
nid: NumaNode,
) -> Result<NonNull<[u8]>, AllocError> { ... }
unsafe fn free(ptr: NonNull<u8>, layout: Layout) { ... }
}
핵심적으로 realloc이 제공 메서드인 alloc과 free의 역할까지 포함해 대부분의 일을 처리하지만, 그 외에는 기존 트레이트와 유사합니다.
여기서 흥미로운 포인트는 Flags 구조체와 NumaNode입니다. 둘 다 모든 할당 요청이 동일하지 않음을 시사합니다. 실제로 커널에는 메모리 할당 시 어떤 일이 일어나야 하는지에 대한 다양한 시나리오가 있습니다.
이는 어떤 할당에 대해서든 _컨텍스트_를 제공할 방법이 있어야 함을 의미합니다. 이는 wg #138에서 논의되고 있으며, 트레이트 스케치도 포함되어 있습니다:
pub unsafe trait Allocator {
type Ctx: Copy;
fn allocate(&self, ctx: Self::Ctx, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
}
Rust 커널 할당자는 이미 늦었다고 생각하지만, 기본 트레이트에 컨텍스트를 포함할지 여부는 강하게 고려할 만한 사항처럼 느껴집니다.
할당자 트레이트의 목표 중 하나는 더 “특이한” 스타일의 할당을 다루는 것입니다. glibc, jemalloc, mimalloc 같은 표준 시스템 스타일 할당자뿐 아니라, 가비지 컬렉션 할당자나 bump allocator 같은 스타일도 지원하고 싶습니다.
bump allocator를 예로 들면, 개별적으로 메모리를 해제하지 않고 한 번에 모두 해제합니다. 즉, 개별 값에 대한 deallocate는 no-op가 됩니다.
하지만 앞서 언급했듯 Box는 Box<T, A>처럼 두 개의 제네릭 인자를 보관합니다. Global 같은 ZST 할당자에선 단일 포인터로 컴파일되니 괜찮지만, 실제로는 deallocate에 쓰이지도 않을 할당자를 필드로 보관하게 되면, 아무 이유 없이 Box의 크기만 커집니다.
working group에서 논의된 한 가지 해결책은 트레이트를 둘로 쪼개는 것입니다: 할당과 해제를 분리합니다.
대략 이런 형태입니다:
pub unsafe trait Deallocator {
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: NonZeroLayout);
}
pub unsafe trait Allocator: Deallocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
}
그리고 할당자를 다른 deallocator로 변환하는 방법이 있다면, 이슈에 있는 zakarumych의 예시처럼 다음과 같이 할 수 있습니다:
// used to keep the lifetime around
struct BumpDealloc<'a> {
_marker: PhantomData<&'a Bump>,
}
unsafe impl<'a> Deallocator for BumpDealloc<'a> {
fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {}
}
impl<'a> From<&'a Bump> for BumpDealloc<'a> {
fn from(_bump: &'a Bump) -> Self {
BumpDealloc { _marker: PhantomData }
}
}
fn foo<'a>(bump: &'a Bump) {
// currently includes `&'a Bump` in the `Box`, making it bigger than a pointer
let box: Box<u32, &'a Bump> = Box::new_in(42, bump);
// now it's pointer sized
let box: Box<u32, Bumped<'a>> = box.into();
assert_eq!(size_of_val(&box), size_of::<usize>());
}
트레이트를 쪼개지 않고도 가능하다는 제안도 있습니다. 예를 들어:
struct BumpDealloc<'a>(PhantomData<&'a Bump>);
unsafe impl Allocator for BumpDealloc<'_> {
fn allocate(&self, _: Layout) -> Result<NonNull<[u8]>, AllocError> {
panic!("cannot allocate through BumpDealloc")
}
unsafe fn deallocate(&self, _: NonNull<u8>, _: Layout) {}
}
하지만 이런 런타임 패닉은 Rust의 정신과 어긋나는 느낌이 있습니다. 따라서 트레이트를 단순하게 유지할지, 아니면 이런 표현력을 허용할지의 트레이드오프입니다.
저는 이 문제에 대해 트레이트를 쪼개는 게 가치 있는지 계속 마음이 왔다 갔다 했습니다. 이슈에서 강한 근거는 no-op deallocator, 혹은 allocator보다 더 적은 공간을 차지하는 deallocator 정도뿐입니다. 즉, 결국 allocator는 항상 필요하고 deallocator는 특수한 페어링이라는 이야기입니다.
만약 도움이 되는 다른 사용 사례가 있다면 고려할 가치가 있을지도 모릅니다. 하지만 사람들이 원하는 일을 막지 않는 한, 트레이트는 단순하게 유지하는 편이 좋다고 생각합니다.
할당은 실패할 수 있고, 기존 트레이트는 에러 타입으로 AllocError를 가진 Result를 반환합니다. 이는 왜 실패했는지에 대한 정보가 거의 없는 ZST 에러로, “실패했다”는 것만 담고 있습니다. 할당이 실패했을 때 더 많은 컨텍스트가 포함되어 호출자가 문제를 복구할 수 있으면 좋겠습니다.
이는 wg #23과 wg #106에서 논의되며, 커스텀 에러 타입을 제공하는 것이 유용한 이유와 사용 사례가 포함되어 있습니다.
연관 에러(associated error)를 넣으면 트레이트는 이렇게 됩니다:
pub unsafe trait Allocator {
type Error: core::error::Error;
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, Self::Error>;
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
// ...other provided methods...
}
물론 다른 대안도 있습니다. 예를 들어 컨텍스트가 있다면 할당 번호를 함께 제공해서 out-of-band로 에러를 가져올 수도 있습니다. 또는 thread local 등에서 last error를 조회할 수도 있지만, 중간에 다른 할당이 끼어들면 오류가 나기 쉬워집니다. 이런 것들은 Result 타입을 본래 의도대로 사용하는 것보다 덜 _좋다_는 느낌입니다.
다만 연관 타입과 관련된 한 가지 이슈는 dyn 호환성을 쉽게 만드는 문제입니다. 연관 에러 타입이 있으면 dyn Allocator가 아니라 dyn Allocator<Error = AllocError>를 곳곳에서 사용해야 합니다. 이게 큰 문제라고는 생각하지 않으며, 표준 라이브러리가 매끄럽게 만들어주는 헬퍼 배관을 제공할 수 있을 것 같습니다. 어쨌든 dyn 호환 할당자를 막을 정도는 아닙니다.
할당자 트레이트의 대안으로 Store API라는 것이 있고, 어느 정도 추진력이 있는 것으로 보입니다. 이는 특히 스택에 올리는 것과 관련해, 기존 트레이트보다 더 많은 사용 사례를 열어줍니다.
하지만(이건 제 개인적인 핫 테이크일 뿐이고 가치가 크진 않습니다!) 저는 이것이 과도하게 설계되었다고 생각하며, 첫 번째 시스템도 아직 나오지 않았는데 second system effect를 겪고 있는 것처럼 보입니다. API 표면적이 더 크고 복잡하며, 5개의 트레이트를 도입하고, _포인터_를 _핸들_로 추상화하는 것은 불필요한 간접화처럼 느껴집니다. 기존 트레이트와 비교했을 때, 간단한 할당자를 이것으로 포팅한다고 상상해 보면 따라가기가 더 어려웠습니다.
기본 트레이트를 확장해 Storage API의 좋은 부분 몇 가지를 지원하는 세계는 가능하다고 보지만, 기존 트레이트 대신 이것을 안정화하는 것은 더 큰 오르막길처럼 느껴집니다. 즉, 좋은 부분을 취해 가능하다면 별도로 안정화하자는 쪽입니다.
이 기능이 오래된 만큼, 안정화를 기다리는 동안의 브리지 솔루션 실험이 나오는 것은 피할 수 없습니다. 그 공백을 메우기 위해 allocator_api2 크레이트가 존재합니다.
이 크레이트는 nightly의 현재 트레이트 정의를 미러링하여, 현 상태의 할당자 트레이트를 stable에서 사용할 수 있게 합니다.
놀랍게도 crates.io에서 2억 5천만 회가 넘는 다운로드를 기록한 인기 크레이트이고, hashbrown과 bumpalo 같은 크레이트의 선택적 의존성(optional dependency)입니다.
이는 실제로 기존 이슈라기보다는, 현재 설계가 가져올 수 있는 파장에 대한 논의입니다. 여기서 말하는 모노모피제이션의 _비용_이란, 코드 생성 측면뿐 아니라 개발자 경험을 말합니다. 모든 컨테이너에 새로운 제네릭 인자가 붙는 것은 호출 지점에서 엄청난 변경을 유발할 것입니다.
예를 들어 현재 Vec<T>는 값 타입만 제네릭이고 그 외는 없습니다. 아래와 같은 시그니처를 가진 함수가 세상에 수백만 개는 있을 겁니다:
fn do_something(input: Vec<String>) {
// something with the vec
}
하지만 커스텀 할당된 vec도 지원하고 싶다면, 이제 함수 시그니처에 트레이트 바운드를 추가해야 합니다:
fn do_something<A: Allocator>(input: Vec<String, A>) {
// something with the vec
}
그뿐만 아니라 서로 다른 할당자를 가진 vec를 섞어 쓰는 것도 꽤 고통스러워집니다. Vec<T, A> != Vec<T, B>이기 때문입니다.
이걸 곳곳에서 조정하는 것은 악몽일 수 있습니다. 그래서 이에 대한 가이드나 해결책이 필요합니다. 저는 다른 언어들이 이를 어떻게 다루는지 조금 조사했는데, 커스텀 할당자를 지원하는 Zig와 C++에 초점을 맞췄습니다.
저는 두 언어 모두 전문가가 아니므로, 이해가 틀렸을 가능성이 큽니다. 하지만 요지는 여기에서 동적 디스패치가 생각만큼 나쁘지 않을 수도 있다는 느낌이 든다는 것입니다.
Zig에서 할당자가 어떻게 동작하는지 보고 싶었습니다. Zig를 쓰는 감성은 메모리 할당에 더 큰 유연성을 제공한다는 것이니까요. 그렇다면 Rust에는 없고 Zig에는 있는 것은 무엇이며, 어떻게 제공할까요?
Allocator는 두 개의 포인터를 가진 구조체입니다. 하나는 상태(state)를 위한 포인터이고, 다른 하나는 const 함수들의 VTable을 가리키는 포인터입니다:
// not really defined like this (but for illustrations only)
pub const Allocator = struct {
ptr: *anyopaque, // opaque pointer to allocator state
vtable: *const VTable, // pointer to constant function table
};
pub const VTable = struct {
alloc: *const fn (*anyopaque, usize, Alignment, usize) ?[*]u8,
resize: *const fn (*anyopaque, []u8, Alignment, usize, usize) bool,
remap: *const fn (*anyopaque, []u8, Alignment, usize, usize) ?[*]u8,
free: *const fn (*anyopaque, []u8, Alignment, usize) void,
};
이는 타입 소거(type erasure)를 이용한 동적 디스패치 스타일의 할당자입니다. 이런 방식은 정적 디스패치보다 느릴 거라고 생각하기 쉽지만, Nical은 블로그 글에서 항상 그렇지는 않다고 말합니다. 때로는 모노모피제이션으로 인한 코드 부풀림(code bloat)이 정적 디스패치의 성능 이점을 압도하기도 합니다.
게다가 LLVM이 많은 경우 어차피 정적 호출로 재작성할 수 있어, 케이크도 먹고 케이크도 가질 수 있는 셈입니다.
그렇다면 Rust에서 비슷한 것을 어떻게 얻을 수 있을까요? 현재 존재하는 트레이트 정의만으로도 dyn Allocator를 사용하면 사실상 같은 스타일의 동적 디스패치를 제공할 수 있고, 함수 호출에서 이를 Allocator 바운드로 사용할 수 있습니다:
fn do_something(input: Vec<T, Box<dyn Allocator>>) {
// something with the vec
}
따라서 Rust에서도 트레이트 정의만으로 이미 동적 디스패치 경로를 선택할 옵션이 있습니다. 물론 object-safety 등 고려할 점이 있지만, 완전히 불가능한 영역은 아닙니다.
C++은 오래된 언어라서, Rust보다 더 많은 결함이 있을 거라고 예상하게 됩니다.
C++도 Rust가 겪을 수 있는 것과 같은 모노모피제이션 문제를 일부 갖습니다. 다른 할당자를 쓰면 컨테이너 타입이 달라지기 때문입니다. 하지만 templates 외에도, C++17에서는 이를 해결하기 위해 polymorphic memory resources (pmr)를 도입했습니다.
예를 들어 아래 C++ 코드는 에러가 납니다.
auto vec1 = std::vector<int,allocator1>();
auto vec2 = std::vector<int,allocator2>();
auto vec = vec1;
vec = vec2;
pmr는 Zig와 Rust의 dyn과 비슷한 방식(내부 구현은 아마 동일하진 않겠지만)으로 타입 소거를 통해 이를 해결합니다:
auto vec1 = std::pmr::vector<int>(&resource1);
auto vec2 = std::pmr::vector<int>(&resource2);
auto vec = vec1;
vec = vec2;
즉 C++은 정적 경로로 시작했다가 동적 디스패치 쪽으로 이동했습니다. 물론 여전히 옛 컨테이너 타입을 사용할 수 있지만, 다른 언어들이 할당자에서 동적 디스패치 쪽으로 기우는 만큼, 우리도 할당자에서 동적 디스패치에 좀 더 주목하는 게 어울릴지도 모릅니다.
Zig에서처럼, 동적 케이스에서 정적으로 인라인할 수 있는 LLVM 트릭이 있다면(또는 Rust에서도 이미 된다면), 그 비용을 생각만큼 치르지 않을 수도 있고, _공짜_일 수도 있습니다.
보시다시피 거의 10년이 지난 지금도, 아직 결정해야 할 것들이 남아 있습니다. 최근 몇 년 동안 안정화를 위한 관성은 확실히 느려졌지만, 여전히 고민이 필요한 영역입니다.
저는 가능한 결과를 몇 가지로 정리해 보는 걸 좋아합니다:
이는 사실상 현재의 상태입니다. 지금의 관성 수준을 도와줄 무언가가 없다면, 또 다른 10년 뒤에도 여기 있을 가능성이 큽니다! 다만 저는 그렇게 비관적이진 않고, 관심만 조금 모이면 충분히 해결할 수 있다고 생각합니다.
현재 트레이트는 이미 유용하며, 사실 allocator_api2 크레이트를 통해 이미 사용 가능합니다. 그리고 최근 트레이트가 크게 바뀌지 않았다는 점은 안정화할 준비가 됐다는 신호일 수도 있습니다. 다만 이것이 nightly에만 있어서 현재 API 소비자가 많지 않기 때문일 수도 있습니다. 즉, 안정화의 리스크는 어떤 사용 사례를 놓치는 것입니다.
저는 이 방향에 부분적으로 찬성합니다. 이 경우 rust for linux 사용 사례와 다른 특수한 사례는 제외될 수 있지만, 그들은 어차피 안정화 없이도 앞으로 나아가고 있습니다.
열려 있는 이슈 75개와 위에서 언급한 주요 블로커들을 다시 보면, 필요한 몇 가지 추가 기능을 지원하도록 트레이트를 약간 조정할 수 있습니다.
저는 일부러 다룰 블로커를 전략적으로 골랐습니다. 왜냐하면 이들은 전부 함께 조합 가능하기 때문입니다.
트레이트 분리는 제외하고, 컨텍스트, non-zero layout, 연관 에러 타입을 포함하면, 대략 이런 형태의 트레이트를 상상할 수 있습니다:
pub unsafe trait Allocator {
type Ctx: Copy;
type Error: core::error::Error;
fn allocate(
&self,
ctx: Self::Ctx,
layout: NonZeroLayout
) -> Result<NonNull<[u8]>, Self::Error>;
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: NonZeroLayout);
// ...other provided methods...
}
이것이 최종 형태여야 한다고 _암시_하는 것은 아니지만, 이런 방향은 충분히 가능한 범위 안에 있다고 생각합니다.
위 블로커들 외에도 Box noalias 같은 unsoundness 이슈가 있는데, 이미 해결되었을 수도 있고, 그 밖에도 트리아지(triage)가 필요한 이슈가 몇 가지 있습니다.
제 접근은 다음과 같을 것 같습니다:
저는 저장소의 이슈들을 직접 처리해 나가는 것이 가장 좋은 전진 경로라고 느낍니다.
현재로서는 커스텀 할당자가 아직 안정화할 상태가 아니며, 가능한 한 빨리 안정화하는 것이 좋겠습니다.
왜 이것이 유용한지에 대해서는 거의 다루지 않았는데, 그 장점은 꽤 자명하다고 느끼기 때문입니다. 제가 관심을 갖는 이유는 rust logfire team에서 일하고, datafusion에 기여하고, 만들고, 그리고 메모리가 없으면 쉽게 크래시 나는 프로덕션 시스템에서의 메모리 문제를 다루기 때문입니다. 제가 하는 모든 일은 커스텀 할당자로부터 이득을 얻고, 어떤 경우에는 저를 막고 있는 것을 풀어줍니다. 중요한 곳에서 할당을 다루기 위해 필요한 도구들이 उपलब्ध하길 바랍니다.
이 글이 적어도 약간의 논의를 촉발하고, 커스텀 할당자에 새 생명을 불어넣기를 바랍니다.