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

"Gary Guo" <[email protected]>
Newsgroups org.kernel.vger.rust-for-linux,dev.linux.lists.nova-gpu,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.