Since the various “placement new” RFC’s were withdrawn last spring, there doesn’t seem to have been much progress on this issue. Would it be possible to have a much simpler solution that doesn’t require any new syntax or traits?
What if we had a single new library function:
ptr::write_with<T, F>(dst: *mut T, f: F) where F: FnOnce() -> T
which would be equivalent to
ptr::write(dst, f());
except that write_with
would be an intrinsic with special compiler support: f()
is evaluated using dst
for the place context, so f’s result is constructed in-place.
Or in implementation terms: when f()
's result would be created on the stack and memcpy’d to dst
, it gets created directly in dst
instead.
That’s pretty much it. Library functions would have to be changed to take advantage of this, e.g. to add Vec::push_with
and Box::new_with
that allocate storage before calling ptr::write(), but it’s all ordinary rust code.
Is this sufficient? It’s a whole lot less machinery than the the various <-
/ in
/ &out
/ Placer
proposals had, but I don’t see what problems those solved that this doesn’t.
An even simpler variation
This could work no new functions at all, by making ptr::write(dst, src)
an intrinsic with special treatment. If src is a value expression, it’s evaluated in a place context with address dst
.
This version might be easier to use and would benefit existing code, but it might be surprising that you can’t write a forwarding wrapper for it without changing its behavior, or write your own functions with the same behavior. vec::push_with
etc. would still have to take closures, so ptr::write_with
seems more symmetric.
Dynamically sized types
I think this could work with dynamically sized types, though there might be complication around who tells who what the actual size is and what happens if it’s wrong.
Does this actually make enough guarantees?
I’m not sure. The spec doesn’t say what happens when a value expression is evaluated with a value context. I’m interpreting that silence to mean that no move happens (and that no temporary memory location is allocated, so there would be nowhere to move from). But I’m not 100% sure of that. Since temporary allocation and moves are mostly unobservable to programs, maybe the compiler still does it sometimes?
(Edit: credit to @vadimcn who suggested the same thing two years ago; apologies for not noticing that sooner.)