-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clarify iterator by_ref docs #135987
base: master
Are you sure you want to change the base?
Clarify iterator by_ref docs #135987
Conversation
r? @ibraheemdev rustbot has assigned @ibraheemdev. Use |
/// | ||
/// This is useful to allow applying iterator adapters while still | ||
/// retaining ownership of the original iterator. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this part of the diff is necessary. The one-liner at the top and the explanation seems sufficient, maybe just adding a disclaimer that the original iterator is still mutated is enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Borrows an iterator, rather than consuming it." is quite poorly phrased, as it is subsequent methods applied to the borrow, that will be prevented from consuming the original iterator.
"This is useful to allow applying iterator adapters while still retaining ownership of the original iterator." is also quite opaque, as it is already possible to do just that with the original iterator, except if the method happens to take self, but that crucial detail is not mentioned.
In addition to this, it is not obvious how the mutable borrow of an iterator turns into an iterator. It wasn't until I started writing this PR that I thought to myself: "wait a minute, there is no reason a mutable borrow of an iterator should be an iterator, unless there is an impl block that says so." Before that, I think I thought something like this: "I must be missing something about how mutable references work, because I don't see how this method works..." It is just like the chars
method on Strings giving a Chars struct which impls iterator, except that there is no &mut impl Iterator
struct to point to, therefore I think it makes sense to link to the relevant impl directly, to show that there is no magic here.
I do agree that the original iterator being mutated is still missing from my text, so I will work on that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you are getting at, but I feel that this is a bit wordy. Let me try rewording it. We should keep a one sentence header for rustdoc, Read::by_ref
does a better job of this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, there should be a short one sentence summary at the top, can do.
/// This turns an iterator into its mutably-borrowed iterator via | ||
/// [impl<I: Iterator + ?Sized> Iterator for &mut I { type Item = I::Item; ...}](trait.Iterator.html#impl-Iterator-for-&mut I). | ||
/// This allows using consuming methods on the mutable borrow, | ||
/// which then consume the mutable borrow instead of the original, | ||
/// such that you can continue to use the original iterator afterwards. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// This turns an iterator into its mutably-borrowed iterator via | |
/// [impl<I: Iterator + ?Sized> Iterator for &mut I { type Item = I::Item; ...}](trait.Iterator.html#impl-Iterator-for-&mut I). | |
/// This allows using consuming methods on the mutable borrow, | |
/// which then consume the mutable borrow instead of the original, | |
/// such that you can continue to use the original iterator afterwards. | |
/// This method returns a mutable borrow of the iterator, which also | |
/// implements the [`Iterator`] trait. This allows you to use iterator adapters | |
/// through the reference, which will mutate but still retain ownership of | |
/// the original iterator. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that it is important to say that the original iterator is mutated when the &mut is used.
@ibraheemdev I've tried to reformulate. Let me know if it is better. |
@rustbot ready |
/// Most `Iterator` methods, other than the `next` method (and this one), | ||
/// are consuming methods; they move the iterator, taking ownership of it, | ||
/// via their `self` parameter (and then mutating it privately). | ||
/// Usually, this is fine, though sometimes you want to | ||
/// mutate the original iterator without giving up ownership of it. | ||
/// That is where this method comes in. | ||
/// | ||
/// The return value of this method acts like the original iterator, | ||
/// except that all consuming methods behave like mutating methods, | ||
/// which mutate the original iterator, without taking ownership of it. | ||
/// | ||
/// This works, because the mutable reference that this method returns, | ||
/// implements the `Iterator` trait in its own right via: | ||
/// [impl<I: Iterator + ?Sized> Iterator for &mut I { type Item = I::Item; ...}](trait.Iterator.html#impl-Iterator-for-%26mut I). | ||
/// This implementation simply passes all method calls on to the original iterator, | ||
/// allowing you to call a consuming iterator method on the mutable reference, | ||
/// which will mutate the original, | ||
/// without giving up ownership of the original iterator. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Most `Iterator` methods, other than the `next` method (and this one), | |
/// are consuming methods; they move the iterator, taking ownership of it, | |
/// via their `self` parameter (and then mutating it privately). | |
/// Usually, this is fine, though sometimes you want to | |
/// mutate the original iterator without giving up ownership of it. | |
/// That is where this method comes in. | |
/// | |
/// The return value of this method acts like the original iterator, | |
/// except that all consuming methods behave like mutating methods, | |
/// which mutate the original iterator, without taking ownership of it. | |
/// | |
/// This works, because the mutable reference that this method returns, | |
/// implements the `Iterator` trait in its own right via: | |
/// [impl<I: Iterator + ?Sized> Iterator for &mut I { type Item = I::Item; ...}](trait.Iterator.html#impl-Iterator-for-%26mut I). | |
/// This implementation simply passes all method calls on to the original iterator, | |
/// allowing you to call a consuming iterator method on the mutable reference, | |
/// which will mutate the original, | |
/// without giving up ownership of the original iterator. | |
/// Creates a "by reference" adapter for this instance of `Iterator`. | |
/// | |
/// The returned adapter also implements `Iterator` and will simply borrow | |
/// this current iterator. Any iteration performed through the reference | |
/// will mutate the original iterator. | |
/// | |
/// Most `Iterator` methods are consuming methods, returning adapters | |
/// that take ownership of the iterator. This method allows you to mutate | |
/// the original iterator without giving up ownership of it, consuming the | |
/// reference instead of the iterator itself. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, consuming methods is not very good, since the book says: Methods that call next are called consuming adapters, because calling them uses up the iterator. The original text already talks about consuming the iterator, which is also no good.
And, Iterator adapters are methods defined on the Iterator trait that don’t consume the iterator. Instead, they produce different iterators by changing some aspect of the original iterator.. You're using the term adapter for the return value of a method here instead of for the method itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, consuming methods is not very good, since the book says: Methods that call next are called consuming adapters, because calling them uses up the iterator. The original text already talks about consuming the iterator, which is also no good.
That part is from your version :) Maybe we can just say "most Iterator
methods return adapters that take ownership of the original iterator" instead?
And, Iterator adapters are methods defined on the Iterator trait that don’t consume the iterator. Instead, they produce different iterators by changing some aspect of the original iterator.. You're using the term adapter for the return value of a method here instead of for the method itself.
I think we use the two interchangeably. For example, the docs of Iterator::for_each
say:
In some cases for_each may also be faster than a loop, because it will use internal iteration on adapters like
Chain
.
We don't have to be too nitpicky with the terminology as long as it gets the point across, I don't want to have three paragraphs to explain that by_ref
returns an iterator that mutates the original when it is used.
@@ -4019,6 +4033,7 @@ where | |||
} | |||
} | |||
|
|||
/// Implements Iterator for mutable references to Iterator, such as those produced by [Iterator::by_ref] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Implements Iterator for mutable references to Iterator, such as those produced by [Iterator::by_ref] | |
/// Implements `Iterator` for mutable references to iterators, such as those produced by [`Iterator::by_ref`]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Another attempt, less wordy and with a one-sentence summary. Also no more confusing uses of consuming. Let me know what you think. |
/// Most `Iterator` methods, other than the `next` method (and this one), | ||
/// take ownership (via a `self` parameter) of the iterator and then mutate it. | ||
/// This method retuns a mutable reference to the original iterator, | ||
/// that acts like the original iterator in all respects, | ||
/// except that ownership-taking methods take ownership of the reference instead of of the original. | ||
/// All mutation by ownership-taking methods will still mutate the original iterator. | ||
/// | ||
/// # Technical details | ||
/// | ||
/// The mutable reference that this method returns, implements the `Iterator` trait via: | ||
/// [impl<I: Iterator + ?Sized> Iterator for &mut I { type Item = I::Item; ...}](trait.Iterator.html#impl-Iterator-for-%26mut I). | ||
/// This implementation simply passes all method calls on to the original iterator. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Most `Iterator` methods, other than the `next` method (and this one), | |
/// take ownership (via a `self` parameter) of the iterator and then mutate it. | |
/// This method retuns a mutable reference to the original iterator, | |
/// that acts like the original iterator in all respects, | |
/// except that ownership-taking methods take ownership of the reference instead of of the original. | |
/// All mutation by ownership-taking methods will still mutate the original iterator. | |
/// | |
/// # Technical details | |
/// | |
/// The mutable reference that this method returns, implements the `Iterator` trait via: | |
/// [impl<I: Iterator + ?Sized> Iterator for &mut I { type Item = I::Item; ...}](trait.Iterator.html#impl-Iterator-for-%26mut I). | |
/// This implementation simply passes all method calls on to the original iterator. | |
/// Most `Iterator` adapters take ownership (via a `self` parameter) of the iterator. | |
/// This method returns a mutable reference to the original, which implements the | |
/// iterator trait by delegating to the original iterator. This allows iterator adapters | |
/// to take ownership of the reference, mutating the original iterator while retaining | |
/// ownership. |
- It's not true that only
next
doesn't take ownership, there are other methods likenth
andadvance_by
. - "and mutate it" isn't exactly true, because the methods themselves don't mutate the iterator. We can just omit this.
- "mutation by ownership-taking methods" is a little awkward, I updated the wording to use "adapters" to refer to the adapter methods explicitly, which I explained take ownership.
- The hyperlink to the iterator implementation doesn't seem to work: Rustdoc can't create link to trait implementation with generics (
[link](#impl-Trait-for-Type<G1, G2>)
) #105600. I added a written explanation instead.
Does that seem reasonable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- It's not true that only
next
doesn't take ownership, there are other methods likenth
andadvance_by
.
That's why the text says "Most .. methods", but I thought it important to mention next
as an exception because it is so fundamental. Of the 76 methods listed only 17 don't take self
, so 59 methods do, which certainly counts as most, no? But I would also be happy with "many" instead of "most"... I see that your proposed replacement differs mostly by replacing "methods" with "adapters" but all of, and advance_by
are adapters and next
and nth
arguably even "iterator adapters" (because Option impls Iterator).
- "and mutate it" isn't exactly true, because the methods themselves don't mutate the iterator. We can just omit this.
It is true! They take ownership and then you have no way of knowing what they do, but in fact they do mutate the iterator, which you can see if you use by_ref to prevent ownership-taking! I think it is important to mention this, because then it cannot be a surprise if after preventing ownership taking these methods still (!!!) mutate the original. They always mutate the original. The only difference is in whether ownership is transferred or not.
- "mutation by ownership-taking methods" is a little awkward, I updated the wording to use "adapters" to refer to the adapter methods explicitly, which I explained take ownership.
The book calls next
an adapter and it does not take ownership. The book names consuming adapters (next and other methods that call next), iterator adapters (methods whose return value impls iterator (next returns an Option which is an iterator, so arguably it is also an iterator adapter)), and other methods (only size_hint?).
- The hyperlink to the iterator implementation doesn't seem to work:
Should work now... thanks for catching!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's why the text says "Most .. methods", but I thought it important to mention next as an exception because it is so fundamental.
My only concern was that "other than the next
method (and this one)" seems to suggest that every other iterator method is consuming.
It is true! They take ownership and then you have no way of knowing what they do, but in fact they do mutate the iterator, which you can see if you use by_ref to prevent ownership-taking
Right, but the methods themselves don't mutate it, they simply return a new iterator adapter/wrapper.
The book calls next an adapter and it does not take ownership. The book names consuming adapters (next and other methods that call next), iterator adapters (methods whose return value impls iterator (next returns an Option which is an iterator, so arguably it is also an iterator adapter)), and other methods (only size_hint?).
Sure, but we already clarified that we are talking about adapters that consume self
in the previous sentence.
Should work now... thanks for catching!
Can we hyperlink to the implementation through the "which also implements the iterator trait" part of the text? I don't think it's worth an entire separate section.
@@ -1825,10 +1825,20 @@ pub trait Iterator { | |||
Inspect::new(self, f) | |||
} | |||
|
|||
/// Borrows an iterator, rather than consuming it. | |||
/// Iterator adapter that turns ownership-taking methods into mutating methods. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Iterator adapter that turns ownership-taking methods into mutating methods. | |
/// Creates a "by reference" adapter for this instance of `Iterator`. |
I know this is a little vague, but the way you've written it reads awkwardly. The original seems fine as well with the new explanation. It's hard to fit exactly what we want to say into one sentence, so I think being general is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I agree that my phrasing is perhaps not good, and the other explanation is now better, so the summary can be simpler. Going off your "by reference" iterator, how about something like: "Iterator adapter that passes on method calls to the original iterator by mutable reference."?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we keep my suggestion for now to mirror io::{Read, Write}::by_ref
and Allocator::by_ref
, and you can open a separate PR to discuss changing it? I don't want to get too hung up on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, done.
Alright, going back to basics. I hope nothing here is controversial. We've both invested way too much time here. |
@rustbot ready |
fixes #95143