Skip to content

Tracking Issue for Iterator::collect_into #94780

@frengor

Description

@frengor
Contributor

Feature gate: #![feature(iter_collect_into)]

This is a tracking issue for adding the collect_into method to the Iterator trait.
Iterator::collect_into lets an iterator to be collected into a collection which implements the Extend trait, consuming the iterator and adding every of its item to the collection.
Adding this method has also the benefit of making the Extend trait more discoverable.

Public API

trait Iterator {
    type Item;

    fn collect_into<E: Extend<Self::Item>>(self, collection: &mut E) -> &mut E
    where
        Self: Sized;
}

Steps / History

  • Final comment period (FCP)
    Stabilization PR

Unresolved Questions

Activity

added
C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
T-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.
on Mar 9, 2022
Corfucinas

Corfucinas commented on May 31, 2022

@Corfucinas

This would be a useful method when using MPSC and appending to different vectors depending on the object.

mqudsi

mqudsi commented on Nov 8, 2022

@mqudsi
Contributor

Not sure if this is where any discussion should go but I would like it to formally request from now that consideration should be given to the ability to (try to?) collect into a fixed-size buffer well, primarily for purposes of avoiding heap allocation. Presumably this would just be a separate feature (try_collect_into?) but if the design of that feature would in any way conflict with the current collect_into feature, then I would like us to discuss these issues here and now before collect_into stabilizes any further.

collect_into is nice because it lets you reduce allocations by directly extending a previous buffer/collection rather than allocating into a wholly separate one and then forcing you to merge the two, which (boilerplate aside) might be less efficient. On the other hand, collecting into a fixed-size buffer could let you use iterators to directly collect into a stack-allocated array/slice, which is pretty much guaranteed to be a huge performance win. I am happy to open a separate issue to specifically track such a feature, but again, I just wanted to mention it here so that if there's something the collect_into feature should (or shouldn't) have in order to facilitate such a try_collect_into in the future, we can hopefully fix it.


The idea is that sometimes you want to use the iterator façade to simplify code transforming an existing collection and your input set is either of a known size or a capped size. Being able to collect into a stack-allocated array or mutable slice thereof would allow collecting the results of an enumeration cleanly. The semantics can (but don't necessarily have to) differ from those of collect_into, as certain things (like tracking the number of elements collected) become harder and there's the question of what to do when the provided fixed-length destination doesn't fit everything (return false and just leave the remainder unread/uncollected in the iterator? panic?).

You can currently mock this yourself by implementing Extend<A> (though the Extend api assumes infallibility) and then using .inspect() before .collect_into() to track the number of elements actually written to the collection.


EDIT:

It may very well just make more sense to loop over the items and add them to the array rather than building a huge façade around that. (Technically the same goes for collect_into as well...)

feature-engineer

feature-engineer commented on Jan 18, 2023

@feature-engineer

Regarding the last point:

Is it worth it to have this API? The Iterator interface is already pretty large, and use cases can easily be written differently without this API.

I have an example use case, and would like to know how it should be written differently without this API:

I'm implementing a limited size priority queue (i.e. I want to keep only the top N elements of the queue at any given time).
For this I create a struct which has a collection and a size as its members.
I create this struct with a given size field - which is why it has to be created before it is being collected to.
Using this queue after implementing its insert fn, and extend fn is trivial when this API exists:

q = SizedQueue::new(5);
<some iter over large data>.collect_into(q);

How would it look with an alternative API?

frengor

frengor commented on Jan 18, 2023

@frengor
ContributorAuthor
q = SizedQueue::new(5);
<some iter over large data>.collect_into(q);

How would it look with an alternative API?

q = SizedQueue::new(5);
q.extend(<some iter over large data>);

The two codes are equivalent (in fact, collect_into is implemented calling Extend::extend).

frengor

frengor commented on Jan 18, 2023

@frengor
ContributorAuthor

Presumably this would just be a separate feature (try_collect_into?)

Yeah, I think it should be. However, seeing that try_collect doesn't handle fallible allocations, the name of either try_collect_into or try_collect should be changed to maintain coherence (also see the unresolved questions of try_collect).

but if the design of that feature would in any way conflict with the current collect_into feature, then I would like us to discuss these issues here and now before collect_into stabilizes any further.

I don't think it will really impact collect_into, I suppose it would mostly require adding something like a TryExtend trait (it would be good to add a TryFromIterator trait, too), but maybe there is a better way to handle fallible allocations (?).

It may very well just make more sense to loop over the items and add them to the array rather than building a huge façade around that. (Technically the same goes for collect_into as well...)

That would probably just be the implementation of TryExtend for slices I think, and having it implemented once in the stdlib doesn't require users to duplicate the same code over and over. collect_into uses Extend for this exact reason.

theemathas

theemathas commented on Jul 6, 2025

@theemathas
Contributor

Is it reasonable to add another method that takes and returns an E instead of a &mut E? That is:

fn collect_into_owned<E: Extend<Self::Item>>(self, collection: E) -> E
where
    Self: Sized,

This would be convenient for doing stuff like .collect_into_owned(Vec::with_capacity(n)) or .collect_into_owned(HashMap::with_hasher(hash_builder)).

In an ideal world, this collect_into_owned method would also be able to accept a &mut Collection argument (in which case we wouldn't need the &mut E version of the method). However, for whatever reason, there's no blanket impl for the Extend trait on &mut _. Adding this blanket impl now would be a breaking change, since &mut is a fundamental type.

For reference, here's a cursory search for code on github that would break if we add the blanket impl.

Is it possible to do anything better about this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCT-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @mqudsi@theemathas@frengor@feature-engineer@Corfucinas

        Issue actions

          Tracking Issue for `Iterator::collect_into` · Issue #94780 · rust-lang/rust