Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> The final design, taking inspiration from C++, would be a form of guaranteed optimization, where constructing a new value and then immediately moving it to the heap causes it to be constructed on the heap in the first place.

Note that there's some discussion about the name of that proposal, because "optimization" gives the wrong idea (that it's optional or could depend on the backend).



I'm probably misunderstanding the complexity of the problem, but wouldn't this be solvable by just defining the right calling convention?

"Any structures larger than x, or any structures marked with a marker type, are returned by the caller providing an outref to a buffer with correct size, and the callee directly writes the structure into that buffer."

Then you could just write normal code like

    fn initialize() -> A { 
    // the initialization code 
    }
and it would just work? And it would reduce unnecessary copies in a lot of other situations too? Given how much work has been put into this issue, and how much less convenient the proposed solutions are, I feel like I must be missing something.


The missing piece is that it would still force you to make even larger types in many cases, such as `Result<Large, Error>`.

Essentially the problem is composability. If you are building a large type from a sequence of other large types, and one or more step is fallible, the normal return ABI breaks down very quickly.


Why make this a behind-the-scene optimization instead of just introducing `new`? That would make things much more clear for everyone.


Because constructors are really weird. Usually in Rust, when a struct is constructed, it already upholds all its invariants because construction is the "last" step in the initialization function. But with a C++-like constructor, it starts with a struct where all fields are in an invalid state, and then the struct's invariants are slowly established field by field. This is kinda impossible to square with Rust's safety promise. Even in safe languages like Java, there are often bugs when one calls other function from the constructor, that now observes the instance under construction violating its usual invariants. And this is also something Rust wants to avoid.


> But with a C++-like constructor, it starts with a struct where all fields are in an invalid state, and then the struct's invariants are slowly established field by field.

AIUI, that's why MaybeUninit<T> exists. But even if you address the issue of it being unsafe to assert that a MaybeUninit has been initialized (which &out references could in principle solve) there are real problems with this; for example, MaybeUninit<T> has no niches or free-for-use padding even when T does, so you can't just "project" MaybeUninit to individual fields except in special cases. My understanding is that C++ partial initialization has the exact same issues in principle, they just don't come up as often because the standard for code correctness is a lot less rigorous.


How does "coalesced heap construction" or "coalesced heap allocation"?


C++ uses the term elision for these kinds of semantics.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: