Skip to content
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

Document the behaviour of infinite iterators on potentially-computable methods #47547

Merged
merged 4 commits into from
Feb 10, 2018

Conversation

varkor
Copy link
Member

@varkor varkor commented Jan 18, 2018

It’s not entirely clear from the current documentation what behaviour
calling a method such as min on an infinite iterator like RangeFrom
is. One might expect this to terminate, but in fact, for infinite
iterators, min is always nonterminating (at least in the standard
library). This adds a quick note about this behaviour for clarification.

cc @rkruppe

…e methods

It’s not entirely clear from the current documentation what behaviour
calling a method such as `min` on an infinite iterator like `RangeFrom`
is. One might expect this to terminate, but in fact, for infinite
iterators, `min` is always nonterminating (at least in the standard
library). This adds a quick note about this behaviour for clarification.
@rust-highfive
Copy link
Collaborator

r? @Kimundi

(rust_highfive has picked a reviewer for you, use r? to override)

@@ -24,6 +24,10 @@ fn _assert_is_object_safe(_: &Iterator<Item=()>) {}
/// This is the main iterator trait. For more about the concept of iterators
/// generally, please see the [module-level documentation]. In particular, you
/// may want to know how to [implement `Iterator`][impl].
///
/// Note: Methods on infinite iterators that generally require traversing every
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it was probably helpful to point this out in the trait documentation, but if this feels like too much of a niche case to mention here, the module-level documentation could be sufficient.

@kennytm kennytm added A-docs Area: documentation for any part of the project, including the compiler, standard library, and tools S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 18, 2018
//! let least = positives.min().unwrap(); // Oh no! An infinite loop!
//! // `positives.min` causes an infinite loop, so we won't reach this point!
//! println!("The least positive number is {}.", least);
//! ```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block should be marked as ```no_run, otherwise I don't think the CI will accept it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, of course!

@@ -24,6 +24,10 @@ fn _assert_is_object_safe(_: &Iterator<Item=()>) {}
/// This is the main iterator trait. For more about the concept of iterators
/// generally, please see the [module-level documentation]. In particular, you
/// may want to know how to [implement `Iterator`][impl].
///
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing white space.

[00:05:27] tidy error: /checkout/src/libcore/iter/mod.rs:300: trailing whitespace
[00:05:27] tidy error: /checkout/src/libcore/iter/mod.rs:306: trailing whitespace
[00:05:27] tidy error: /checkout/src/libcore/iter/iterator.rs:27: trailing whitespace

@@ -25,6 +25,10 @@ fn _assert_is_object_safe(_: &Iterator<Item=()>) {}
/// generally, please see the [module-level documentation]. In particular, you
/// may want to know how to [implement `Iterator`][impl].
///
/// Note: Methods on infinite iterators that generally require traversing every
/// element to produce a result may not terminate, even on traits for which a
/// result is determinable in finite time.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that the trait's doc-comment is essentially just a redirect to the module docs, I don't think this note needs to be here as well. Maybe mention diverging in fold specifically, as the most general case of the methods that diverge when given infinite input?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was divided 50:50 on this one. I've taken it out and replaced it with a comment on fold as you suggested.

@@ -298,7 +298,21 @@
//!
//! This will print the numbers `0` through `4`, each on their own line.
//!
//! Bear in mind that methods on infinite iterators, even those for which a
//! result can be computed in finite time, may not terminate. Specifically,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the words "can be computed" lead to a "well, how do I compute it then?" feeling. Perhaps it could be phrased along the lines of "even if a value for them could be determined mathematically", or something?

And instead of "may not terminate", what about being more explicit, with something like "will not return successfully"? (I don't know if talking about the different ways something could diverge would be useful.)

//! ```no_run
//! let positives = 1..;
//! let least = positives.min().unwrap(); // Oh no! An infinite loop!
//! // `positives.min` causes an infinite loop, so we won't reach this point!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: in debug mode this panics due to overflow, and doesn't infinite-loop
https://play.rust-lang.org/?gist=bff007ce8e876616bbd965ec4c6dcba0&version=stable

@abonander
Copy link
Contributor

Has there been discussion on whether or not to override methods like this, e.g. Iterator::min() for RangeFrom, so that they do terminate?

@varkor
Copy link
Member Author

varkor commented Jan 19, 2018

@abonander: There's been some quite extensive discussion about that very recently — check #47370 and #47362 out for some of the background.

@bluss
Copy link
Member

bluss commented Jan 21, 2018

Small quibble! RangeFrom<i64> is not infinite, it's a problematic category of its own.

repeat(0) is infinite. RangeFrom<i64> does not repeat values and it uses a finite size integer; from the docs:

Note: Currently, no overflow checking is done for the Iterator implementation; if you use an integer range and the integer overflows, it might panic in debug mode or create an endless loop in release mode. This overflow behavior might change in the future.

Thus we can say it's not reasonable to use 0.. etc as an infinite iterator, reasonable use says you should not attempt to go past the wrap around.

@varkor
Copy link
Member Author

varkor commented Jan 21, 2018

I think RangeFrom is under-documented in general. It claims that "[RangeFrom] contains all values with x >= start", which implies that it might be finite (ending at the maximum value) for non-infinite types (like u64). I don't think it's unfair to claim it's an infinite iterator (at least with the current guarantees, the description here is correct as far as I can tell), but perhaps a repeat(0) would be better.

@bluss
Copy link
Member

bluss commented Jan 21, 2018

It is not infinite -- that leads to an argument that (1i32..).take(n).max() will complete successfully for all n -- but it does not, it might panic. It's only easier to trip over this for smaller integer types.

@varkor
Copy link
Member Author

varkor commented Jan 21, 2018

Yeah, that's a fair point. I'll rework the example to use repeat, which seems safer.

(On a related, but off-topic point, is there a reason [other than backwards compatibility at this stage] that RangeFrom couldn't/shouldn't be finite for integers? In fact, the documentation for RangeFrom seems to suggest that the behaviour for overflow/reaching the limit could change in the future.)

@scottmcm
Copy link
Member

Small quibble! RangeFrom is not infinite

This was discussed before in #42315 (comment).

It's "infinite" in the sense of "no matter how many times you call it, it will never return None". I don't think it makes sense to consider panicking when discussing iterator lengths, because then you can't even say that repeat(0).map(f) is infinite, or that (0..10).map(f) has length 10.

is there a reason that RangeFrom couldn't/shouldn't be finite for integers?

It's hard, potentially impossible, to implement without either making the struct bigger or making it not return every possible number for the type (ie being 0..256u8, not 0..=256u8). That's one of the many places where the fact that ranges are Iterator instead of IntoIterator is a bit unfortunate.

@bluss
Copy link
Member

bluss commented Jan 22, 2018

@scottmcm That sounds fine in relation to that change, but saying it is infinite is not correct for all situations. I like my argument still. In your code, if the user knows their f, they know if the iterator is finite. If they don't know their f, then yes, they can't predict if its iteration will finish or diverge.

@shepmaster
Copy link
Member

Ping from triage, @Kimundi !

@kennytm
Copy link
Member

kennytm commented Jan 31, 2018

Triage ping. Since @Kimundi haven't left any comments on this PR, could @rust-lang/docs or @rust-lang/libs reassign this to another member? Thanks.

@oberien
Copy link
Contributor

oberien commented Jan 31, 2018

Coming from the previous discussion, I don't know if defining the current behaviour is the correct way. As soon as it's in the documentation, we can't change it as easily. Instead I still like the potential of optimizations for cases like Repeat, but as discussed previously, an RFC is needed.

@steveklabnik
Copy link
Member

I'd like libs to take it, as we need to know what behavior is desired in order to document it.

@kennytm kennytm added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. and removed A-docs Area: documentation for any part of the project, including the compiler, standard library, and tools labels Jan 31, 2018
@dtolnay
Copy link
Member

dtolnay commented Jan 31, 2018

I think it is valuable to document this behavior, and I like the way @varkor has phrased this as an informative warning rather than as a firm commitment for the standard library to always preserve the behavior that certain methods loop infinitely. I really like the "determined mathematically" suggestion from @scottmcm and I am glad that was incorporated. Let's merge when someone from docs is okay with this as well.

@kennytm kennytm added A-docs Area: documentation for any part of the project, including the compiler, standard library, and tools and removed T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Jan 31, 2018
@pietroalbini
Copy link
Member

Could someone from @rust-lang/libs review this?

@alexcrichton
Copy link
Member

@pietroalbini oh I think we're good on the libs front! @dtolnay mentioned above and I suspect many other @rust-lang/libs members would agree, so at this point I think we're looking for a sign-off from @rust-lang/docs.

@GuillaumeGomez
Copy link
Member

Looks good for me.

@shepmaster
Copy link
Member

@alexcrichton We have a sign-off from docs; is that sufficient? Or were you looking for entire team discussion?

@frewsxcv
Copy link
Member

just read this over as well, and it seems reasonable to document this! one question i have is why fold gets the added documentation but not other Iterator methods that also exhaust the iterator, but it's a minor point that shouldn't block this PR my opinion. i don't think we need the rest of thee docs team to sign off on this so i'm going to approve this. thanks for your contribution @varkor! 💟

@bors r+ rollup

@bors
Copy link
Contributor

bors commented Feb 10, 2018

📌 Commit f129374 has been approved by frewsxcv

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 10, 2018
kennytm added a commit to kennytm/rust that referenced this pull request Feb 10, 2018
…oc, r=frewsxcv

Document the behaviour of infinite iterators on potentially-computable methods

It’s not entirely clear from the current documentation what behaviour
calling a method such as `min` on an infinite iterator like `RangeFrom`
is. One might expect this to terminate, but in fact, for infinite
iterators, `min` is always nonterminating (at least in the standard
library). This adds a quick note about this behaviour for clarification.
bors added a commit that referenced this pull request Feb 10, 2018
@bors bors merged commit f129374 into rust-lang:master Feb 10, 2018
@varkor varkor deleted the infinite-iterators-warning-doc branch February 10, 2018 23:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-docs Area: documentation for any part of the project, including the compiler, standard library, and tools S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.