Helper traits for many clone pattern

I've often seen code that uses many clones for one value, i.e. something like:

func1(item.clone());
func2(item.clone());
func3(item);

Now that const generics and array destructuring are stable, in might be nice to add a trait to the std/core prelude like the following:

pub trait CloneMany<const N: usize>: Clone {
    fn clone_many(self) -> [Self; N];
}

impl<T: Clone, const N: usize> CloneMany<N> for T {
    fn clone_many(self) -> [Self; N] {
        todo!()
    }
}

#[derive(Clone)]
struct X {}

fn main() {
    let x = X{};
    let [first, second, third] = x.clone_many();
    func1(first);
    func2(second);
    func3(third);
}

It's not necessary or anything, but it seems cleaner in my mind, and importantly prevents people from having to omit that final clone that unaligns everything.

If you're taking self by value, it might make more sense to call this into_clones or something similar. As is, it reads like a normal .clone() call, which takes &self. (Alternatively, it could act like a normal .clone call, but then you'd get a needless extra clone sometimes.)

I'm not sure how valuable is inserting that into std, but you can create one for yourself easily:

pub trait CloneMany: Clone {
    fn clone_many<const N: usize>(self) -> [Self; N];
}

impl<T: Clone> CloneMany for T {
    fn clone_many<const N: usize>(self) -> [T; N] {
        let mut result: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
        if let [items @ .., last] = result.as_mut_slice() {
            for item in items {
                *item = MaybeUninit::new(self.clone());
            }
            *last = MaybeUninit::new(self);
        }
        unsafe { (&result as *const _ as *const [T; N]).read() }
    }
}

Playground.

As a new trait it's unlikely -- the bar for traits is pretty high -- but I actually have a PR open for the functionality as a function: Add `resize(_with)` and `(try_)repeat(_with)` for arrays by scottmcm · Pull Request #91506 · rust-lang/rust · GitHub

let [first, second, third] = array::repeat(x);
func1(first);
func2(second);
func3(third);

Though until Irrefutable slice patterns should be inferred as arrays of that length · Issue #76342 · rust-lang/rust · GitHub is fixed it'll probably need a length annotation.

2 Likes

Honestly having array::repeat would be sufficient. It's just as clear and has a wider use case.

Yeah I agree; this is what I was looking for

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