That's not quite right. Rust has two ways to allocate a Box.
One is the `box` keyword: this guarantees that the object will be constructed in place on the heap, but doesn't work with fallible allocators (or, in fact, anything but the default allocator). This is what the research paper you linked is talking about. However, all these years later, `box` has never been stabilized; nor will it be in its current form, because it's considered too inflexible. Whatever form of in-place construction does eventually get stabilized will likely support fallibility.
The other way to allocate a Box is `Box::new`. This is not compiler magic; it's simply a regular function, implemented in Rust, that calls the allocator and then moves (i.e. memcpys) an existing object into the new allocation. If you write your own Box-like type, there's nothing stopping you from making your `new` function fallible.
What about optimizations? Does `Box::new` get optimized in ways that a fallible version won't? Well, no. The compiler will inline `Box::new`, and if you call it with a fresh stack allocation as an argument, LLVM can theoretically, sometimes, elide the stack allocation and the memcpy altogether, instead initializing the object directly on the heap. Theoretically. The paper claims that it always does so, but the paper is wrong. [1] In fact, the compiler doesn't do so even in relatively easy cases. [2] It would be nice if LLVM did better here, but it doesn't seem to be a big source of overhead in Rust programs in practice. If LLVM did improve the optimization, it would probably work equally well for a fallible allocator as for `Box::new`, because they're equally complex from its perspective: `Box::new` can panic, and LLVM treats panics as branches.
(Box does have compiler magic for a different case: the ability to move out of it. Not being able to replicate that in a custom type is suboptimal, but not the end of the world.)
As for why those OS projects panic on allocation failure:
The phil-opp.com one implements the standard allocator interface in order to use the standard library container types. I think it sucks that Rust's standard containers don't support allocation failure, but you don't need to use them...
For Redox... I'm not actually sure what they're doing, but I think "fn oom" is a hook for users of the allocator to call if they want to panic on out-of-memory, not something that mandates panicking. At least, that's the behavior of `std::alloc::handle_alloc_error` [3], which was moved there from being a trait method on `GlobalAlloc` named `oom`. However, they're implementing a different `oom` on an old version of the `Alloc` (not `GlobalAlloc`) trait, which is unstable; that method was removed entirely well over a year ago, so I guess the code must be out of date.
One is the `box` keyword: this guarantees that the object will be constructed in place on the heap, but doesn't work with fallible allocators (or, in fact, anything but the default allocator). This is what the research paper you linked is talking about. However, all these years later, `box` has never been stabilized; nor will it be in its current form, because it's considered too inflexible. Whatever form of in-place construction does eventually get stabilized will likely support fallibility.
The other way to allocate a Box is `Box::new`. This is not compiler magic; it's simply a regular function, implemented in Rust, that calls the allocator and then moves (i.e. memcpys) an existing object into the new allocation. If you write your own Box-like type, there's nothing stopping you from making your `new` function fallible.
What about optimizations? Does `Box::new` get optimized in ways that a fallible version won't? Well, no. The compiler will inline `Box::new`, and if you call it with a fresh stack allocation as an argument, LLVM can theoretically, sometimes, elide the stack allocation and the memcpy altogether, instead initializing the object directly on the heap. Theoretically. The paper claims that it always does so, but the paper is wrong. [1] In fact, the compiler doesn't do so even in relatively easy cases. [2] It would be nice if LLVM did better here, but it doesn't seem to be a big source of overhead in Rust programs in practice. If LLVM did improve the optimization, it would probably work equally well for a fallible allocator as for `Box::new`, because they're equally complex from its perspective: `Box::new` can panic, and LLVM treats panics as branches.
(Box does have compiler magic for a different case: the ability to move out of it. Not being able to replicate that in a custom type is suboptimal, but not the end of the world.)
As for why those OS projects panic on allocation failure:
The phil-opp.com one implements the standard allocator interface in order to use the standard library container types. I think it sucks that Rust's standard containers don't support allocation failure, but you don't need to use them...
For Redox... I'm not actually sure what they're doing, but I think "fn oom" is a hook for users of the allocator to call if they want to panic on out-of-memory, not something that mandates panicking. At least, that's the behavior of `std::alloc::handle_alloc_error` [3], which was moved there from being a trait method on `GlobalAlloc` named `oom`. However, they're implementing a different `oom` on an old version of the `Alloc` (not `GlobalAlloc`) trait, which is unstable; that method was removed entirely well over a year ago, so I guess the code must be out of date.
[1] https://users.rust-lang.org/t/how-to-create-large-objects-di...
[2] https://play.rust-lang.org/?version=stable&mode=release&edit... (press "..." -> "ASM")
[3] https://doc.rust-lang.org/nightly/std/alloc/fn.handle_alloc_...