[Pre-RFC] Partially Initialized Types


#1

there’s been a lot of talk about &uninit references, MaybeUninit, placement-new…

I’d like to propose an alternative to all of them: Partially Initialized Types. They’re, arguably, the cleanest approach to all of this.

Consider types:

struct Foo { a: i32, b: i32 }
struct Bar { a: i32, b: Foo }

Then, we can have:

let x: Bar(b(a)) = Bar { b: Foo { a: 1 } };

However, passing these to functions is mostly pointless:

fn foo(x: &mut Bar()) {
    // can't do anything with x here
}
foo(x); // but can still call it

So we need a way to specify type/state mutations:

fn bar(x: &mut Bar() -> Bar(a)) {
  x.a = 2;
}
bar(x);

assert_eq!(x.a, 2);
assert_eq!(x.b.a, 1);

What you write and how the compiler looks at it are slightly different: if we accept - to mean uninitialized, ~ to mean ignored, and + to mean initialized, we generally have:

Foo() = Foo(~a, ~b)
Foo(a) = Foo(+a, ~b)
fn foo(x: Foo() -> Foo(b)) = fn foo(x: Foo(~a, -b) -> Foo(~0, +1))
fn foo(x: Foo(b) -> Foo()) = fn foo(x: Foo(~a, +b) -> Foo(~a, -b))

With ~ being taken as - in object position (non-references, basically - return types, let bindings, etc). So:

let x: Foo(a) = ...;
let y: Foo() = x; // deinitializes x.a, without dropping x.

You can then have, with specialization:

fn place_back() -> Place<T()> {
    // allocate stuff
}
impl Drop for Place<T()> {
    fn drop(&mut self) {
        self.vec.deallocate();
    }
}
impl Drop for Place<T(..)> {
    fn drop(&mut self) {
        // it's initialized, don't do anything
    }
}

Finally, this is the discussion that led to this: https://github.com/rust-lang/rfcs/pull/2534