Pre-RFC: Optional FIelds

There are two major semantic problems here:

  • ? should primarily imply Result, not Option, and
  • ? acts like a monad bind, while here its simply a conversion.

The correct way to instantiate a struct without writing Some everywhere is to use .into().

I'd suggest exploring the = None draft with some ..} syntax that permits not fully initializing like Default requires. In fact, we could just aim for flexibility if we're going to do this:

/// Partial initialization of struct-like `struct`s and `enum`s
///
/// Rust magically remembers which members `partial_default`   
/// initializes which members it does not initialize.
/// If `T: PartialDefault` then you can initialize `T` like
/// `T { foo_1, .., foo_n, ... }` provided that `foo_1, .., foo_n` cover
/// all members not initialized by `T: PartialDefault`.
///
/// In this example, Rust initializes `foo_1, .., foo_n` before invoking
/// `partial_default`, so `partial_default` could inform its initializing
/// assignments by reading `foo_1, .., foo_n`.   In particular, if `T` is
/// an `enum` then Rust sets the `enum` variant before invoking
/// `partial_default`, so that  `partial_default` knows which variant it
/// constructs.  `#[derive(Default)]` invokes `partial_default` if available.
///
/// Inside `partial_default`, we caution against creating a `&mut Self`
/// via `let self = unsafe { self.assume_init_mut() }`.  Instead we
/// suggest creating a `*mut Self` with `let self = self.as_mut_ptr();`
/// from which you project and write to individual fields, like
/// ```
/// unsafe { core::ptr::write(
///     addr_of_mut!((*self).field),  // Please note &mut (*self).field as *mut FieldType, risks UB
///     value
/// ) };
/// ```
/// This handles `Drop` fields correctly.
///
/// Only supports struct-like `struct`s and `enum`s, not tuple-like,
/// and never other types.  We mark `partial_default` unsafe because
/// it initializes drop fields without dropping their existing contents.
/// In particular `PartialDefault` cannot "reset" types unless `Self: Copy`.
unsafe trait PartialDefault {
    unsafe fn partial_default(self: &mut MaybeUninit<Self>);
}

In this way, you could specify remarkably flexible behaviors with manual unsafe impl PartialDefault for MyType, while also supporting proc macros that performed more convenient initialization. Rust could provide = None as a special case for Option and perhaps = more generally.

1 Like