# Pre-RFC: Function Variants

**URL:** <https://internals.rust-lang.org/t/pre-rfc-function-variants/16732>\
**Category:** language design\
**Created:** [June 1, 2022, 10:55pm UTC](https://internals.rust-lang.org/t/pre-rfc-function-variants/16732 "2022-06-01T22:55:24Z")\
**Posts on this page:** 4\
**Page:** 2

<div class="post-metadata">

**Author:** ![Nemo157](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nemo157/32/11585_2.png) [@Nemo157](https://internals.rust-lang.org/u/Nemo157)\
**Post date:** [June 8, 2022, 7:58am UTC](https://internals.rust-lang.org/t/pre-rfc-function-variants/16732/21 "2022-06-08T07:58:20Z")

</div>

You can already use doc-comments on the `impl` blocks to create groupings.

---

<div class="post-metadata">

**Author:** ![kpreid](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kpreid/32/8484_2.png) [@kpreid](https://internals.rust-lang.org/u/kpreid)\
**Post date:** [June 9, 2022, 4:40am UTC](https://internals.rust-lang.org/t/pre-rfc-function-variants/16732/22 "2022-06-09T04:40:41Z")

</div>

> [@dpaoliello](#):
>
> Using a builder pattern is interesting, but it has a few issues:
> 
> - It doesn't help with different options having different return types …
> - It's difficult to encode that options are mutually exclusive without using runtime errors. You could use different types (e.g., once you use `uninit()` you get a `BoxBuilderUninit` which doesn't have a `zeroed` method), but then you're multiplying the different return types issue per each one of these types.

Both of these are entirely possible with builders that make use of type parameters. For example, the following builder allows you to build any kind of `Box` that `std` currently offers, has _almost_ no redundant methods (the redundant `build()` → `try_build()` forwarding can be fixed by making `try_build()` into a trait method), does not allow any mutually exclusive options to be chosen, and does not have any run-time errors from incorrect builder use.

```rust
#![feature(allocator_api)]
#![feature(new_uninit)]
use std::{
    alloc::{AllocError, Allocator, Global},
    marker::PhantomData,
    mem::MaybeUninit,
};

pub struct BoxBuilder<I = (), A = Global> {
    initializer: I,
    allocator: A,
}

impl BoxBuilder<(), Global> {
    pub const fn new() -> Self {
        BoxBuilder {
            initializer: (),
            allocator: Global,
        }
    }
}

impl<I> BoxBuilder<I, Global> {
    /// Set the allocator the box will use.
    pub fn allocator<A>(self, allocator: A) -> BoxBuilder<I, A> {
        BoxBuilder {
            initializer: self.initializer,
            allocator: allocator,
        }
    }
}

/// Methods that determine the value type and means of initialization.
impl<A> BoxBuilder<(), A> {
    /// Set the initial value that will be moved into the box.
    pub fn value<T>(self, value: T) -> BoxBuilder<Init<T>, A> {
        BoxBuilder {
            initializer: Init { value },
            allocator: self.allocator,
        }
    }

    pub unsafe fn from_raw<T: ?Sized>(self, ptr: *mut T) -> BoxBuilder<FromRaw<T>, A> {
        BoxBuilder {
            initializer: FromRaw { ptr },
            allocator: self.allocator,
        }
    }

    pub fn uninit<T: ?Sized>(self) -> BoxBuilder<Mu<T>, A> {
        BoxBuilder {
            initializer: Mu {
                zeroed: false,
                len: (),
                _p: PhantomData,
            },
            allocator: self.allocator,
        }
    }

    pub fn zeroed<T: ?Sized>(self) -> BoxBuilder<Mu<T>, A> {
        BoxBuilder {
            initializer: Mu {
                zeroed: true,
                len: (),
                _p: PhantomData,
            },
            allocator: self.allocator,
        }
    }
}

impl<T, A: Allocator> BoxBuilder<Init<T>, A> {
    pub fn build(self) -> Box<T, A> {
        self.try_build().unwrap()
    }

    pub fn try_build(self) -> Result<Box<T, A>, AllocError> {
        Box::try_new_in(self.initializer.value, self.allocator)
    }
}

impl<T, A: Allocator> BoxBuilder<FromRaw<T>, A> {
    /// Build a box with Sized contents.
    pub fn build(self) -> Box<T, A> {
        // Safety: self.initializer.ptr was given to us via an unsafe method.
        unsafe { Box::from_raw_in(self.initializer.ptr, self.allocator) }
    }
}

impl<T, A: Allocator> BoxBuilder<Mu<T, ()>, A> {
    /// Make the box be of a slice of MaybeUninit with the given length.
    /// Whether it is zeroed is determined by the previous call to uninit() or zeroed().
    pub fn len(self, len: usize) -> BoxBuilder<Mu<T, usize>, A> {
        BoxBuilder {
            initializer: Mu {
                zeroed: true,
                len,
                _p: PhantomData,
            },
            allocator: self.allocator,
        }
    }

    /// Build a box containing MaybeUninit.
    pub fn build(self) -> Box<MaybeUninit<T>, A> {
        self.try_build().unwrap()
    }

    /// Build a box containing MaybeUninit.
    pub fn try_build(self) -> Result<Box<MaybeUninit<T>, A>, AllocError> {
        if self.initializer.zeroed {
            Box::try_new_zeroed_in(self.allocator)
        } else {
            Box::try_new_uninit_in(self.allocator)
        }
    }
}

impl<T, A: Allocator> BoxBuilder<Mu<T, usize>, A> {
    /// Build a box with a slice of a specified length, uninitialized or zeroed.
    pub fn build(self) -> Box<[MaybeUninit<T>], A> {
        if self.initializer.zeroed {
            Box::new_zeroed_slice_in(self.initializer.len, self.allocator)
        } else {
            Box::new_uninit_slice_in(self.initializer.len, self.allocator)
        }
    }
}

pub struct Init<T> {
    value: T,
}
pub struct Mu<T: ?Sized, L = ()> {
    zeroed: bool,
    len: L,
    _p: PhantomData<fn() -> T>,
}
pub struct Uninit;
pub struct Zeroed;
pub struct FromRaw<T: ?Sized> {
    ptr: *mut T,
}

```

I don't mean to claim that this exact builder is a good way to redesign the `Box` API (though [it's been thought of](https://github.com/rust-lang/wg-allocators/issues/90), as I found out after writing the above and then noticing `try_new_uninit_slice_in` doesn't exist); only that builders _can_ handle compile-time checking of both mutually exclusive and independent options, without any more code duplication than the problem actually demands.

---

<div class="post-metadata">

**Author:** ![tema2](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/tema2/32/6498_2.png) [@tema2](https://internals.rust-lang.org/u/tema2)\
**Post date:** [June 9, 2022, 4:37pm UTC](https://internals.rust-lang.org/t/pre-rfc-function-variants/16732/23 "2022-06-09T16:37:02Z")

</div>

Also, with recent RFC that allowed to make safe impls of unsafe trait we can make an unsafe trait for `new` and `try_new` and implement it safely when appropriate.

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [September 7, 2022, 4:37pm UTC](https://internals.rust-lang.org/t/pre-rfc-function-variants/16732/24 "2022-09-07T16:37:18Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.

[Previous page](https://internals.rust-lang.org/t/pre-rfc-function-variants/16732.md?page=1)
