Why no `Extend<A> for &mut Vec<A>` in std?

an example of what I want to do ergonomically:

struct Containers {
    a: Vec<usize>,
    b: Vec<u8>,
}

impl Containers {
    fn update(&mut self) {
        let contents_iter = [(0, 9), (1, 8), (2, 7)].into_iter();

        let mut tuple = (&mut self.a, &mut self.b);
        // this requires satisfying the `&mut Vec: Extend` bound
        tuple.extend(contents_iter);
    }
}

I'm wondering if there are specific reasons for the lack of this implementation

Interesting. I wasn't aware there is even an impl of Extend for tuples. Turns out it was added not so long ago (rust 1.56). There are also no impls for tuples more complex than just pairs.

Regarding your question, there shouldn't be an impl of Extend for &mut Vec<T> specifically. Instead, there should be a generic impl

impl<A, C: Extend<A>> Extend<A> for &mut C { ... }

which simply dereferences the receiver in the function impls. Why no such impl was added, I do not know. I don't see any reasons not to have it, so the likely explanation is just that no one proposed it. It's not like Extend is used that often, and it's even less likely to be used with dynamic dispatch, which is the primary reason to have impls for reference types.

1 Like

Beside the direct impl on Container, you can also do this:

    fn update(&mut self) {
        let contents_iter = [(0, 9), (1, 8), (2, 7)];

        let mut tuple = (std::mem::take(&mut self.a), std::mem::take(&mut self.b));
        // let mut tuple = (&mut self.a, &mut self.b);
        tuple.extend(contents_iter);

        self.a = tuple.0;
        self.b = tuple.1;
    }

The reason to not have &mut T: Extend may be orphan rules. (Well, incorrect reason for the magic std. So I don't know either.)

// Same API as Extend
pub trait Extend2<A> {
    fn extend2<T>(&mut self, iter: T)
    where
        T: IntoIterator<Item = A>;
}

// This doesn't work.
// error[E0119]: conflicting implementations of trait `Extend2<_>` for type `&mut _`
//   --> src/main.rs:48:1
//   |
// 41 | impl<A, E: Extend<A>> Extend2<A> for E {
//   | -------------------------------------- first implementation here
// ...
// 48 | impl<A, E: Extend<A>> Extend2<A> for &mut E {
//   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&mut _`
//   |
//   = note: downstream crates may implement trait `std::iter::Extend<_>` for type `&mut _`
impl<A, E: Extend<A>> Extend2<A> for E {
    fn extend2<T>(&mut self, iter: T)
    where
        T: IntoIterator<Item = A>,
    {
        <Vec<A> as Extend<A>>::extend(self, iter);
    }
}

But we can still use the same API for reference types:

impl<A, E: Extend<A>> Extend2<A> for &mut E {
    fn extend2<T>(&mut self, iter: T)
    where
        T: IntoIterator<Item = A>,
    {
        <E as Extend<A>>::extend(self, iter);
    }
}

impl<A, B, E: Default + Extend<A>, F: Default + Extend<B>> Extend2<(A, B)> for (&mut E, &mut F) {
    fn extend2<T>(&mut self, iter: T)
    where
        T: IntoIterator<Item = (A, B)>,
    {
        let e = std::mem::take(self.0);
        let f = std::mem::take(self.1);
        let mut ef = (e, f);
        <(E, F) as Extend<(A, B)>>::extend(&mut ef, iter);
        let (e, f) = ef;
        *self.0 = e;
        *self.1 = f;
    }
}

    fn update2(&mut self) {
        let contents_iter = [(0, 9), (1, 8), (2, 7)];

        let mut tuple = (&mut self.a, &mut self.b);
        tuple.extend2(contents_iter); // works
    }

Rust Playground

I think most uses of Extend either don't take a type implementing Extend (for example they also bound by Default, see Iterator::unzip) or already take a &mut reference (since there's no point in taking a type that implements Extend by value), so nobody felt the need for this implementation. Tuples are one of the very few exceptions to this rule, since even if you only need a &mut (Vec<usize>, Vec<u8>) you cannot create one from a &mut Vec<usize> and a &mut Vec<u8> (well, there's std::mem::take, but then you need a placeholder or something like take_mut/replace_with). That said, the implementation is possible, that is this compiles (if put in the standard library):

impl<T, ExtendT> Extend<T> for &mut ExtendT
where
    ExtendT: Extend<T>,
{
    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
        (*self).extend(iter)
    }
    fn extend_one(&mut self, item: T) {
        (*self).extend_one(item)
    }
    fn extend_reserve(&mut self, additional: usize) {
        (*self).extend_reserve(additional);
    }
}

Though this is technically a breaking change as it's possible for someone to have implemented Extend for both MyType and &mut MyType.

5 Likes

Setting aside the reasons for the implementation being missing for a moment, something like this should work (untested, on mobile):

struct Containers {
    a: Vec<usize>,
    b: Vec<u8>,
}

impl Containers {
    fn update(&mut self) {
        let contents_iter = [(0, 9), (1, 8), (2, 7)].into_iter();

        let mut tuple = (mem::take(&mut self.a), mem::take(&mut self.b));
        tuple.extend(contents_iter);
        (self.a, self.b) = tuple;
    }
}

Empty Vecs don’t allocate, so it shouldn’t have much overhead compared to your desired version.

3 Likes

At this point, it's also a breaking change to add it since &mut C is a fundamental type, allowing downstream implementations.

3 Likes

If only we had specialization...

This is the problem.

It should exist, but that wasn't noticed back in the day, and we can't do it now, sadly.

It would really help to have it for things like collect_into, so that one thing could be both by-value and by-refmut.

1 Like

We could add it for specific collections though, like &mut Vec as OP asked, and encourage downstream collections to follow suit.

2 Likes

Out of curiosity, would it be possible to introduce the implementation as part of a new edition? My intuition would be no, because if my crate uses edition 2024 (with the added implementation) and has a dependency on a crate with edition 2021 exposing a collection with impl Extent<T> for &mut Collection and impl Extend<T> for Collection, the compiler wouldn't know which implementation for &mut Collection to choose? Which would basically be specialization again, no?

I don't think the trait solver is able to vary by edition.

1 Like

Actually, I think we can't add it for specific collections either, because coherence allows the Extend<_> parameter to be downstream too, e.g. playground:

pub struct Foo;

impl Extend<Foo> for &mut Vec<Foo> {
    fn extend<T: IntoIterator<Item = Foo>>(&mut self, _: T) {
        todo!()
    }
}
2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.