Re: [PATCH 1/6] rust: alloc: add Vec::push_init

From: Gary Guo

Date: Wed Aug 19 2026 - 08:15:07 EST


On Wed Aug 19, 2026 at 12:23 PM BST, Danilo Krummrich wrote:
> On Mon Aug 17, 2026 at 2:56 PM CEST, Eliot Courtney wrote:
>> + pub fn push_init<E>(&mut self, init: impl Init<T, E>, flags: Flags) -> Result<(), E>
>> + where
>> + E: From<AllocError>,
>
> This signature rejects impl Init<T, Infallible>, which is the reason why we have
> e.g. Box::init() and Box::try_init() with different fallible signatures.
>
> So, if we follow InPlaceInit, it'd be
>
> pub fn push_init<E>(&mut self, init: impl Init<T, E>, flags: Flags) -> Result<(), Error>
> where
> Error: From<E>;
>
> and
>
> pub fn try_push_init<E>(&mut self, init: impl Init<T, E>, flags: Flags) -> Result<(), E>
> where
> E: From<AllocError>;
>
> In theory we could also simplify it to
>
> pub fn push_init(&mut self, init: impl Init<T>, flags: Flags) -> Result<(), AllocError>
>
> and
>
> pub fn try_push_init<E>(&mut self, init: impl Init<T, E>, flags: Flags) -> Result<(), E>
> where
> E: From<AllocError>,
>
> However, InPlaceInit actually achieves more with the init() and try_init()
> distinction:
>
> What init() accepts, but try_init() does not accept:
> - impl Init<T, Infallible>
> - impl Init<T, E> where Error: From<E> but NOT E: From<AllocError>
>
> What try_init() accepts, but init() does not accept:
> - impl Init<T, E> where E: From<AllocError> but NOT Error: From<E>
>
> So, with the simplification we'd technically lose out on the
>
> impl Init<T, E> where Error: From<E> but NOT E: From<AllocError>
>
> case.
>
> In any case, init() and try_init() seem a bit mixed up on their purpose
> regarding fallibility and error type strategy, but in order to really cover all
> cases I think it is necessary.

I think this might also be solvable with a new trait? Something like this:

/// Trait indicating how two distinct types should be unified.
trait Unify<Other>: Sized {
type Unified: From<Other> + From<Self>;
}

/// Types can be unified with themself.
impl<T> Unify<T> for T {
type Unified = T;
}

macro_rules! unify_rule {
($ty:ty; $($o:ty => $u:ty)*) => {
impl From<Infallible> for $ty {
fn from(v: Infallible) -> Self {
match v {}
}
}

impl Unify<Infallible> for $ty {
type Unified = $ty;
}

impl Unify<$ty> for Infallible {
type Unified = $ty;
}

$(impl Unify<$o> for $ty {
type Unified = $u;
}

impl Unify<$ty> for $o {
type Unified = $u;
})*
}
}

macro_rules! unify {
($a: ty, $b: ty) => {
<$a as Unify<$b>>::Unified
}
}

unify_rule!(Error; );
unify_rule!(AllocError; Error => Error);

and you just need to write

pub fn push_init<E>(&mut self, init: impl Init<T, E>, flags: Flags) -> Result<(), unify!(E, AllocError)>

This looks like a lot of work to impl, but realistically this is just an
additional trait impl near where you'd the put the `From` impl.

We can even provide an attribute macro `#[unify]` so you just need to stick it
on your `From` impl, e.g.

#[unify]
impl From<AllocError> for Error {
...
}

would generate

impl Unify<AllocError> for Error { ... }
impl Unify<Error> for AllocError { ... }

Best,
Gary