# Another idea for placement new

**URL:** <https://internals.rust-lang.org/t/another-idea-for-placement-new/21604>\
**Category:** language design\
**Created:** [September 26, 2024, 10:48pm UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604 "2024-09-26T22:48:23Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [September 26, 2024, 10:48pm UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/1 "2024-09-26T22:48:23Z")

</div>

**Disclaimer** : This is still in the brainstorming phase, and I need to know if there are any major problems I missed. Please don't bikeshed syntax details for now 🙂

* * *

I've been thinking about placement new, and I realized that we "just" need to extend move analysis with two things:

```rs
struct MyStruct {
  foo: String,
  bar: [u64; 1024],
}

fn use_my_struct(_: MyStruct) {
    todo!();
}

// THIS WORKS TODAY
let mut s = MyStruct { foo: String::new(), bar: [0; 1024] };
// move the fields out of the struct
drop(s.foo);
drop(s.bar);
// move new values into the struct
s.foo = String::from("hello world");
s.bar = [13; 1024];
use_my_struct(s);

```

Rust ensures that all fields are present when `use_my_struct` is called. What's missing is a way to create a struct instance without its fields:

```rs
let mut s: MyStruct = empty!(); // built-in macro
// move new values into the struct
s.foo = String::from("hello world");
s.bar = [13; 1024];
use_my_struct(s);

```

And a way to initialize it from another function; this can be solved with what I call **move references** :

```rs
let mut s: MyStruct = empty!();
init_my_struct(&move s);
use_my_struct(s);

fn init_my_struct(s: &move MyStruct) {
    s.foo = String::from("hello world");
    s.bar = [13; 1024];
}

```

## What's a move reference?

A move reference is similar to a mutable reference. However, it can be uninitialized at first. Once it has been fully initialized, it turns into a normal mutable reference.

Move references need to be **linear types** -- they can be neither copied, nor destroyed:

```rust
{
    let mut s: MyStruct = empty!();
    let r = &move s;
} // error! `r` goes out of scope here, but it can't be dropped

{
    let mut s: MyStruct = empty!();
    let mut r = &move s;
    *r.foo = String::from("hello world");
    *r.bar = [13; 1024];
} // this is ok, because `r` is fully initialized and turned into `&mut MyStruct`

```

This guarantees that functions that accept a move reference and do not return it need to initialize them. We can deduce from the lifetimes which move references are initialized by a function:

```rust
// initializes a, but not b
fn f<'a, 'b>(a: &'a move X, b: &'b move Y) -> &'b Z;

```

`a` has to be initialized by the function `f`, as that is the only way to drop it.

## How would this work with `Box` and `Vec`?

Move references can be created from raw pointers, so it's possible to allocate memory on the heap and obtain a move reference to it.

To initialize a `Box<MyStruct>`, we can add a function that accepts an initializer function:

```rust
impl<T> Box<T> {
    fn init(initializer: impl FnOnce(&move T)) -> Self;
}

let boxed_struct = Box::init(|mut r| {
    *r.foo = String::from("hello world");
    *r.bar = [13; 1024];
});

```

Here, `r` points to a newly created allocation on the heap. `r` being a move reference guarantees that it is initialized within the closure. A similar approach is possible for `Vec`:

```rust
impl<T> Vec<T> {
    fn init(len: usize, initializer: impl FnMut(&move T, usize)) -> Self;
}

let struct_vec = Vec::init(20, |mut r, index| {
    *r.foo = String::from("hello world");
    *r.bar = [index as u64; 1024];
});

```

## How can we enforce that move references aren't dropped?

The problem is that values of generic types can be dropped, like here:

```rust
fn drop<T>(value: T) {}

```

We need to make it impossible to call `drop` with a move reference. This can be achieved with a `Destroy` auto trait that is (like `Sized`) implicitly added to all generics. Since `T` does not have a `?Destroy` bound, we can't call `drop` with linear types. `Destroy` is implemented for all types except move references (and types containing move references).

## How about types other than structs?

Tuples, structs and unions can be initialized by their fields. Enums can be initialized in place like this:

```rust
let mut opt: Option<MyStruct> = Some(empty!());
if let Some(value) = &move opt {
    *r.foo = String::from("hello world");
    *r.bar = [index as u64; 1024];
} else {
    unreachable!()
}

```

Arrays and slices can _not_ be initialized in a loop, since the compiler couldn't prove that every index was initialized. Instead, helper methods can be added:

```rust
impl<'a, T> &'a move [T] {
    fn fill_uninit(self, value: T) where T: Clone;
    fn fill_uninit_with(self, initializer: FnMut(&'a move T, usize));
}

```

## Custom DSTs

A problem with custom DSTs is that you can't easily create them. Move references could alleviate this problem:

```rust
struct MySuperSlice {
    info: u32,
    data: [u8],
}

// passing `<&mut MySuperSlice as Pointee>::Metadata` as first parameter
let boxed_super_slice = Box::init_unsized(1024, |mut r| {
    r.info = 30;
    r.data.fill_uninit(17);
});

```

## Panic safety

When an initializer panics, a value may be left in an uninitialized state. However, since `&move T` does not implement `UnwindSafe`, this should never be observable.

---

<div class="post-metadata">

**Author:** ![HjVT](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/hjvt/32/10365_2.png) [@HjVT](https://internals.rust-lang.org/u/HjVT)\
**Post date:** [September 27, 2024, 1:55am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/2 "2024-09-27T01:55:00Z")

</div>

```rs
impl<T> Box<T> {
    fn init(initializer: impl FnOnce(&move T)) -> Self;
}

let boxed_struct = Box::init(|mut r| {
    *r.foo = String::from("hello world");
    *r.bar = [13; 1024];
});

```

I don't think this provides emplacement at all. Nothing here guarantees that String will be constructed in unitialized `*r.foo` place, rather than on the stack, and then moved there.

---

<div class="post-metadata">

**Author:** ![Ddystopia](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ddystopia/32/10915_2.png) [@Ddystopia](https://internals.rust-lang.org/u/Ddystopia)\
**Post date:** [September 27, 2024, 6:11am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/3 "2024-09-27T06:11:54Z")

</div>

I think it's already covered by view types suggestion here: [https://smallcultfollowing.com/babysteps/blog/2024/06/02/the-borrow-checker-within/](https://smallcultfollowing.com/babysteps/blog/2024/06/02/the-borrow-checker-within/)

I did not understand how to make an anchor to those headers, so "Step 3: View types and interprocedural borrows"

> To me, the biggest open question for view types is how to accommodate “strong updates” to types. I’d like to be able to do let mut wf: {} WidgetFactory = WidgetFactory {} to create a WidgetFactory value that is completely uninitialized and then permit writing (for example) wf.counter = 0. This should update the type of wf to {counter} WidgetFactory. Basically I want to link the information found in types with the borrow checker’s notion of what is initialized, but I haven’t worked that out in detail.

---

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [September 27, 2024, 8:53am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/4 "2024-09-27T08:53:43Z")

</div>

> [@HjVT](#):
>
> I don't think this provides emplacement at all. Nothing here guarantees that String will be constructed in unitialized `*r.foo` place, rather than on the stack, and then moved there.

The content of the `String` is in a separate allocation anyway, and having 3 `usize` on the stack is not a big problem. But I think LLVM can easily remove the stack allocation, because the pointer to the box allocation is already available.

The main reason why `Box::new([1; 1_000_000])` can't be optimized to write the array directly on the heap is that the heap application occurs _after_ the array is created. The `Box::init` api fixes this.

---

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [September 27, 2024, 9:06am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/5 "2024-09-27T09:06:42Z")

</div>

> [@Ddystopia](#):
>
> I think it's already covered by view types suggestion here

Ok, so an initializing function would look like this:

```rust
fn init(s: {} MyStruct) -> MyStruct

```

I really like this idea. The only problem I see with it is that passing around owned values is typically more expensive than passing references.

---

<div class="post-metadata">

**Author:** ![SkiFire13](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/skifire13/32/7579_2.png) [@SkiFire13](https://internals.rust-lang.org/u/SkiFire13)\
**Post date:** [September 27, 2024, 11:18am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/6 "2024-09-27T11:18:03Z")

</div>

FYI the name "move references" has generally been used for references you can move out from, not move **in**. What you described here has been discussed in the past with the name of "out references".

> [@Aloso](#):
>
> ## How can we enforce that move references aren't dropped?
> 
> The problem is that values of generic types can be dropped, like here:
> 
> ```rust
> fn drop<T>(value: T) {}
> 
> ```
> 
> We need to make it impossible to call `drop` with a move reference. This can be achieved with a `Destroy` auto trait that is (like `Sized`) implicitly added to all generics. Since `T` does not have a `?Destroy` bound, we can't call `drop` with linear types. `Destroy` is implemented for all types except move references (and types containing move references).

This does not account for implicit drops due to unwinding. I don't think you can really avoid them, so the only way to fix it is to ensure that the uninitialized place is not accessed during unwinding except for dropping those and only those fields that were initialized before it started.

---

<div class="post-metadata">

**Author:** ![HjVT](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/hjvt/32/10365_2.png) [@HjVT](https://internals.rust-lang.org/u/HjVT)\
**Post date:** [September 27, 2024, 8:40pm UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/7 "2024-09-27T20:40:03Z")

</div>

Yes, it doesn't matter much for `String`. What I'm pointing out is that this proposal is in no way an alternative to placement new.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [September 28, 2024, 5:47pm UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/8 "2024-09-28T17:47:34Z")

</div>

I do think gradual/partial initialization should be possible in the language. It's already possible to do `let s; s = MyStruct { foo, bar }`, but not `let s: MyStruct; s.foo = foo; s.bar = bar;`. This is actually independent of the ability to do so across function boundaries.

I've played some with a "`DefaultForOverwrite`" trait, which works like `Default` (makes a valid instance of the type) but doesn't have a semantic meaning for the "default" value (safe but unspecified state), just an expectation that it's "cheap" to make. It works but isn't particularly pretty.

* * *

Any proposal that includes adding new types should consider pinned initialization and if it beats out just a library approach. My current favorite to point at is [moveit](https://crates.io/crates/moveit) (it seems the most featureful) but there are others as well.

My current feeling is that both `init->uninit` refs and `uninit->init` refs are possible in the language. `init->uninit` should IMO behave exactly like a theoretical `Box<_, PhantomBorrow<'a>>` and have responsibility to drop or leak the value, but cannot safely reference pinned values. `uninit->init` should IMO act more like a C++ lvalue reference, and only exist as a property of function signatures: the function's `name` binding is a place that directly aliases the out place, otherwise acting as if it were just a local binding, the difference being that at normal function exit it's required to have been initialized and isn't dropped; ownership is given to the caller's place. In the event of an unwind, drops happen on local places (including the loaned one) as normal, and the caller's place hasn't been initialized.

Is any of that worth the language complexity to do? I have no idea at this point. _(Necessary disclaimer: I'm a part of the Rust Project on T-opsem. However, this gives no special insight nor say in the design of new language features.)_

---

<div class="post-metadata">

**Author:** ![afetisov](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/afetisov/32/8508_2.png) [@afetisov](https://internals.rust-lang.org/u/afetisov)\
**Post date:** [September 29, 2024, 1:32am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/9 "2024-09-29T01:32:35Z")

</div>

> [@CAD97](#):
>
> if it beats out just a library approach

Generally, I don't think that beating a library implementation would be hard for a proper language feature. `moveit` is a very ingenious library, but frankly its api is horrible. It's better than nothing if you're stuck with implementing C++ constructors, but I'd never use it if I can avoid it. A proper feature with pretty syntax and compiler-enforced safety wouldn't have that issue.

What do you mean by "consider pinned initialization"? It seems to me that if we had `&out T` with reasonable semantics (ignoring the subtle details for now), then pinned inititialization would just deal with `Pin<&out T>` using the usual `Pin` API. Do you think there is some special subtle issue here?

> [@CAD97](#):
>
> In the event of an unwind, drops happen on local places (including the loaned one) as normal, and the caller's place hasn't been initialized.

The usual issue is what if the function panics, but the (transitive) caller doesn't know it? I.e. `f` calls `g` calls `h`, the latter panics, but `g` catches the panic with `catch_unwind`. Now the invariant "if `h` returns, the uninit place was initialized" is violated, and there is no reasonable way to pass this info to `g`, nevermind `f`.

---

<div class="post-metadata">

**Author:** ![quaternic](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/quaternic/32/10440_2.png) [@quaternic](https://internals.rust-lang.org/u/quaternic)\
**Post date:** [September 29, 2024, 3:04am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/10 "2024-09-29T03:04:11Z")

</div>

> [@afetisov](#):
>
> The usual issue is what if the function panics, but the (transitive) caller doesn't know it? I.e. `f` calls `g` calls `h`, the latter panics, but `g` catches the panic with `catch_unwind`. Now the invariant "if `h` returns, the uninit place was initialized" is violated, and there is no reasonable way to pass this info to `g`, nevermind `f`.

The invariant is not violated, because `h` did not return. The control flow that unwinds out of the call to `h` has not initialized the place. If that would lead to `g` returning normally without initializing it some other way, `g` won't compile.

Now, in current Rust you can't directly handle unwinding as control flow, and just from the signature of `catch_unwind` (takes a closure) the compiler can't assume that it would even call that closure.

---

<div class="post-metadata">

**Author:** ![197g](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/197g/32/7276_2.png) [@197g](https://internals.rust-lang.org/u/197g)\
**Post date:** [September 29, 2024, 4:17am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/11 "2024-09-29T04:17:42Z")

</div>

The apt name should be 'return reference' or 'result reference'. Using out has a slight potential to be confused with 'moving-out-of' which is exactly _not_ allowed and because the intended semantic model is quite close to literally that of return values.

This view also relates to `catch_unwind` if the property that the place was initialized must be part of the function signature at the _return value_ somehow. Since catching unwind changes that type to a ` Result<R>` it clearly does not provide the necessary semantics for a caller to absoultely rely on initialization. And that also propagates for wrapper so that on error no suitable 'value' proving initialization is returned, which forces any wrapper to explicitly handle it by fallback initialization (which it probably can't do very well, the owning pointer to the uninitialized slot was moved with the call—the inverse of the old problem of trying to `replace` utilizing an `FnOnce(T) -> T`).

That said, I don't think the view works out practically. The optimal semantic fit might be have multiple disjoint return values however this may run into much worse issues than colored functions where a lot of previously written code is not composable with these new function types (include `core` primitives, including `catch_unwind` ironically).

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [September 29, 2024, 5:35am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/12 "2024-09-29T05:35:15Z")

</div>

One thing I didn't see mentioned in the OP at all is `Result`/`?`.

To me, the trickiest part of all this is making `*p = foo()?;` be in-place, and I have no idea how to make that happen.

(Just having APIs for `Box<MU<T>> + T -> Box<T>` kinds of things is much easier.)

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [September 29, 2024, 6:05am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/13 "2024-09-29T06:05:22Z")

</div>

Of course, there's the C++ approach of just ignoring it and requiring "constructors" to be infallible or panic. Rust would be a little better, since instead of C++'s

```cpp
System sys = {};
auto result = sys.init();

```

Rust could have

```rust
let sys;
let result = System::init(&mut sys);

```

which looks very similar, but differs in that instead of `System` having an intrinsic default "null state," `init` gets (has) to assign the valid-but-unspecified state to the provided return reference before returning an err result.

It's of course still far from ideal — it'd be preferable to not have to make a default value to fill at all — but it's marginally better than _always_ creating a default to then overwrite, even in the success case.

A different view of the problem space that seems like it could almost work is instead:

```rust
impl System {
    fn init(this: &return MaybeUninit<Self>) -> Result<&move Self, SystemInitError>;
}

// used as:
let sys;
let sys = System::init(&mut sys)?;

```

where `&return T` is the `uninit->init` reference and `&move T` is the `init->uninit` reference.

C# combines `out` params with bool-returning `Try*` functions, IIRC, and optionally has strict null checks as part of the compiler. It could be interesting to see how they handle it. (My understanding of newer C# features like strict null checking is merely hearsay.)

---

<div class="post-metadata">

**Author:** ![Vorpal](https://avatars.discourse-cdn.com/v4/letter/v/aca169/32.png) [@Vorpal](https://internals.rust-lang.org/u/Vorpal)\
**Post date:** [September 29, 2024, 7:04am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/14 "2024-09-29T07:04:09Z")

</div>

One radical way to solve this would to take a cue from Swift. My understanding is that Result/Option over there has a special ABI for returns, where the flag is returned in a separate register.

As I don't know swift I don't know if they have niche optimisation, but the way I see it for Rust, we could have such a special ABI only when the niche isn't optimised.

This would also make Result/Option special. But I think you could extend this to any 2 variant enum, potentially even to more than 2 variants. That would remove the specialness.

A question thst remains is how to handle `Result<Option<T>, E>` though. In that case you could collapse the outer enum into 3 states in that dedicated register (when the niches aren't optimised).

So I think in place construction is doable for results, but would require the Rust ABI to not just be a thin wrapper around LLVM C ABI. And it would be a lot of work.

---

<div class="post-metadata">

**Author:** ![afetisov](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/afetisov/32/8508_2.png) [@afetisov](https://internals.rust-lang.org/u/afetisov)\
**Post date:** [September 29, 2024, 9:50am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/15 "2024-09-29T09:50:47Z")

</div>

> [@scottmcm](#):
>
> the trickiest part of all this is making `*p = foo()?;` be in-place

My opinion is that this kind of magic just shouldn't be supported.

The relatively simple and reliable way to do in-place initialization is to take an explicit pointer to the uninitialized place as a parameter. I.e. on stable the function would take `place: &mut MaybeUninit<T>`, and with a proper language support it would be `place: &in T`. The called function can now initialize the place in whatever way it sees fit: it can pass it around to other functions, split into smaller places, store it in structs, pass to FFI calls. Once the function guarantees that the place is initialized, it must use some out of band signalling to pass this information upwards. So basically it should return some special "initialization token", which also automatically handles issues of panic safety, since unwinding will not create those tokens or pass them to callers. And finally, once the original caller gets that "initialized" signal, it can safely call `assume_init` on that uninitialized place.

That's simple, reliable, flexible and maximally composable, working just the same in any context (generators, async). Fallible initialization should be handled in an ordinary way, with initializers returning `Result`, where the initialization token is included only inside the `Ok` variant.

Yes, it won't support the whimsical `*p = f();` syntax, but who needs that, really? The hope of people proposing such syntax is usually that inplace initialization could somehow be magically supported for arbitrary function calls, but an arbitrary function can handle initialization in arbitrary ways, which can't conform to any extra rules without extra effort. At best, maybe we could force the return value to always be generated inplace in the final location. But a lot of huge, possibly self-referential data could be passed around in the function before the returned value is produced. There is no hope to defeat those copies in general, and they defeat the purpose of in-place initialization.

Basically, at best we could hope for some variant of NRVO, and it's just fundamentally not flexible or reliable enough for all use cases, even if we could somehow make it better than C++.

I had a sketch of safe delayed initialization written for a while. Unfortunately, there is a small but crucial bit of compiler magic that I currently don't know how to reasonably implement. Perhaps I should publish the write-up anyway.

---

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [September 29, 2024, 10:03am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/16 "2024-09-29T10:03:15Z")

</div>

The problem is not the ABI. The problem is that when you write `*p = foo()?;` (`p` being an out reference), it doesn't compile because `p` isn't initialized in the error case. There's no way to make initializing functions fallible under this proposal.

Maybe initializers need to return the value – this is more similar to [placement by return](https://github.com/PoignardAzur/placement-by-return/blob/placement-by-return/text/0000-placement-by-return.md). Then the challenge is make an ABI that guarantees that the return value is placed directly into the allocation, without first putting it on the stack.

---

<div class="post-metadata">

**Author:** ![afetisov](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/afetisov/32/8508_2.png) [@afetisov](https://internals.rust-lang.org/u/afetisov)\
**Post date:** [September 29, 2024, 11:07am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/17 "2024-09-29T11:07:32Z")

</div>

> [@Aloso](#):
>
> Move references need to be **linear types** -- they can be neither copied, nor destroyed

Any proposal which starts with "we add linear types" is imho immediately impossible. The core issue is panic safety: values are automatically dropped during unwinding. How do you reconcile it with linear types? Do you forbid panicking while values of those types exist? But most things in Rust can panic, so how would you handle it in code? See [The Pain Of Real Linear Types in Rust](https://faultlore.com/blah/linear-rust/).

Since that post was written, we also have an extra complication: async. Values which are held over `.await` points become part of the future struct, and that struct can be dropped anywhere anytime. Do undroppable values mean that some futures become undroppable? The async design & ecosystem aren't ready for that, even if it could be benefical.

Excluding panics is also a non-starter. There is a lot written about possible `no_panic` annotation or NoPanic effect, although I couldn't find any definitive reference. In short, the subset of Rust which defintely doesn't panic is so small it can't be used for anything interesting. OOM panics, arithmetics operations panic, collection indexing panics, I/O panics. Maybe very rarely, but it's very hard to formally remove from functions.

> [@Aloso](#):
>
> We can deduce from the lifetimes which move references are initialized by a function:
> 
> ```rust
> // initializes a, but not b
> fn f<'a, 'b>(a: &'a move X, b: &'b move Y) -> &'b Z;
> 
> ```

That could only work if `&'a move X` is invariant w.r.t. `'a`, unlike `&'a X` or `&'a mut X`, which are covariant. That sounds a bit confusing, but perhaps possible.

> [@Aloso](#):
>
> However, since `&move T` does not implement `UnwindSafe`, this should never be observable.

`UnwindSafe` is a safe trait, so it can't be relied on for memory safety. In particular, one can always wrap `&move T` in `AssertUnwindSafe`.

> [@Aloso](#):
>
> helper methods can be added:
> 
> ```rust
> impl<'a, T> &'a move [T] {
> fn fill_uninit(self, value: T) where T: Clone;
> fn fill_uninit_with(self, initializer: FnMut(&'a move T, usize));
> }
> 
> ```

That's unsound, because `Clone` or `initializer` may panic in the middle of the slice, and you won't know which part is left uninitialized. This kind of initialization can only work for `Copy` types.

---

<div class="post-metadata">

**Author:** ![SkiFire13](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/skifire13/32/7579_2.png) [@SkiFire13](https://internals.rust-lang.org/u/SkiFire13)\
**Post date:** [September 29, 2024, 11:22am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/18 "2024-09-29T11:22:07Z")

</div>

> [@afetisov](#):
>
> Once the function guarantees that the place is initialized, it must use some out of band signalling to pass this information upwards. So basically it should return some special "initialization token", which also automatically handles issues of panic safety, since unwinding will not create those tokens or pass them to callers.

I can't remember how it was called, but there is a crate that implements inplace (probably pinned too?) initialization with this pattern. It requires marking the tokens with an invariant existential lifetime, like the one used in `GhostCell`, in order to guarantee that the returned token is the correct one.

---

<div class="post-metadata">

**Author:** ![afetisov](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/afetisov/32/8508_2.png) [@afetisov](https://internals.rust-lang.org/u/afetisov)\
**Post date:** [September 29, 2024, 11:34am UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/19 "2024-09-29T11:34:38Z")

</div>

Makes sense. Two issues I see with a pure-library approach:

- It can't take ownership of a place, since `&move` references are currently not a thing. This means that it likely requires boxing. Stack places could be only handled via `&mut T`, so no ownership semantics. Although perhaps it could handle owned stack places the same way `moveit` does?
- There is no safe way to do partial initialization of structs via library-only approach, since there is no way to be generic over the set of fields at the library level.

EDIT: `moveit` does something similar with its stack slots [[1]](https://docs.rs/moveit/latest/moveit/slot/index.html).

---

<div class="post-metadata">

**Author:** ![Aloso](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/aloso/32/5039_2.png) [@Aloso](https://internals.rust-lang.org/u/Aloso)\
**Post date:** [September 29, 2024, 12:01pm UTC](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604/20 "2024-09-29T12:01:20Z")

</div>

> [@afetisov](#):
>
> Any proposal which starts with "we add linear types" is imho immediately impossible. The core issue is panic safety

Maybe they aren't "proper" linear types, but panic safety isn't a problem in practice. A function accepting a move reference (or out reference) must have initialized it by the time it returns, but only _if it returns_. If it panicks, aborts, or loops indefinitely, the uninitialized value can never be observed. Move references can't be moved in `catch_unwind`'s closure, because it has no `?Destroy` bound.

> [@afetisov](#):
>
> Since that post was written, we also have an extra complication: async. Values which are held over `.await` points become part of the future struct

This would mean that the future itself would become `!Destroy`, so the type system would prevent you from dropping the future. Maybe async runtimes won't support `?Destroy` futures at all, so you'll get an error whenever you try to spawn a future holding a move reference across an `.await`. In practice, I don't think initializers ever need to be async, so I don't see a problem here.

> [@afetisov](#):
>
> `UnwindSafe` is a safe trait, so it can't be relied on for memory safety.

You're right. The actual reason why it won't work is that `catch_unwind` implicitly requires `F: Destroy`.

> [@afetisov](#):
>
> That's unsound, because `Clone` or `initializer` may panic in the middle of the slice, and you won't know which part is left uninitialized.

Again, this is not observable as far as I can tell. The only potential issue is a double free, but I'd argue to just leak the entire slice if initializing it panics. Or do you have an example where this would cause UB?

[Next page](https://internals.rust-lang.org/t/another-idea-for-placement-new/21604.md?page=2)
