-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Impl Copy for Range #2848
Comments
Clone it? |
That's clearly less ergonomic than just letting the range be copy. having range itself be an Iterator and not turn into an iterator is very obviously a design flub at this point, and hopefully an RFC can fix it in some later edition. |
This is not possible to fix in an edition (I think), so this is non-actionable. Ranges are explicitly not |
Yandros proposed in the issue linked in the OP how, in an edition change, you could remove Iterator from being on Range directly and then give it Copy. And honestly ive never been clear on what's allowed as an edition change or not. This proposal would be able to compile to the same MIR layer as not using this proposal, so my basic understanding is that it should be possible with an edition change, even if the libs team doesn't end up doing it. |
Adding a
|
I really don't want to do this since my struct is copied over 150 times throughout the code base. I'm using RangeWrapper in the meantime.
Can you expand on this? Why doesn't Copy play well with Iterator? |
Is there any actual soundness issues with an Is it just to avoid Or is it some performance concern because a tree of loops gets expensive fast? How would that come up? |
This is not a soundess question. Iterator and Copy are both safe APIs, so I don't understand how soudness or the copyability of references would be involved here.. We don't want iterators to be Copy because they are stateful objects, and a Copy stateful object can be copied accidentally, causing unexpected behavior. |
It more seems like there should've been proper IntoIter'd types for ranges then instead of having ranges directly be iterators. Though that would've possibly not been all too ergonomic either. |
I see: It's not a performance concern either, so much as a correctness concern. It'd be too easy to make mistakes. It's the same reason Yes some simple wrapper type like |
Yeah, in hindsight IntoIterator impls would have been a better approach. In addition to the Copy problem, there's some jank in RangeInclusive that we would have been able to avoid. |
At least doing this does not break for loop syntax, so nothing too problematic really, but.. I'm afraid |
We cannot remove |
Just want to chime in to add a little bit of anecdata, which is that I have now on at least three occasions had to search out why |
@cmyr Yeah, same, which has led to things like this: https://docs.rs/grep-matcher/0.1.4/grep_matcher/struct.Match.html#indexing |
Presumably, with the necessary compiler support, we could backwards-compatibly add Once a warning exists, implementing |
Errmm, perhaps not. Maybe the common case is indeed in the context where |
One option to reduce churn would be to combine this with an edition change: edition 2018 would warn if the This way existing code will continue to work without warnings. |
For posterity this issue gained some increased interest due to this blog post recently: https://ridiculousfish.com/blog/posts/least-favorite-rust-type.html which was discussed on Hacker News and Reddit. |
I can't find it now but I recall the concern being mostly about Isn't that solved with a very specific lint? Quoting myself from HN:
|
@eddyb I'm actually curious now why an iterator should not be copy. Mostly because I would have expected if that was an actual concern in practice we would have seen a clippy lint against it by now. |
|
I suppose we can generalize the lint to "calling a by-value method (including I wouldn't even mind making But if we want to ignore
|
cc @ridiculousfish who may be interested in this since he wrote the post |
If I understand, there are two choices here, either some wrapper that makes all range types
or else a wrapper that makes range types
In either case, could the various range short hands Abstractly Yet, there lots of code like Also, could |
@burdges To be clear, my I'll try implementing it soon and open a PR, so that we can at least e.g. do a Crater run, and judge it on its merits. |
Opened rust-lang/rust#77180 for the |
I was thinking: Why use |
@programmerjake I think there were issues with those attributes that relying on traits could've resolved, but also it doesn't really matter either way, an attribute could also work, the only difference is if we ever stabilize it. |
I think we should plan on stabilizing it as an attribute, since I've also had iterators that could be |
Update on the experiment I was doing in rust-lang/rust#77180: Quoting myself from that PR:
Importantly, I don't have a strong opinion on "attribute vs trait", and both implementation strategies should work (the attribute might be slightly easier even). I'd suggest looking for previous discussions of |
If anyone is wondering, part of the reason adding trait Trait {}
impl<T: Copy> Trait for T {}
impl Trait for std::ops::Range<usize> {} That snippet produces (on playground) an error with this note:
|
Because While we still can't feature-gate individual There's one issue I see with this: if a dependency uses
cc @nikomatsakis @matthewjasper Do you have any opinions on this strategy? I think if we can feature-gate |
I know the timeline is most likely OK, but I just want to point out that once specialization lands, we will have negative reasoning, and so such a change would no longer be backwards-compatible. |
Since there was talk of introducing new traits. I wanted to share what I think is the real problem: Copy conflates two things. One would be a CloneIsMemcpy trait, which is only useful for perf, and the other is a AutoClone trait, which says that dereferencing a |
@Nadrieril this seems completely orthogonal to the Range/Copy problem, since the problem with iterators being |
If the original problem was
then the fact that Range is CloneIsMemcpy allows: #[derive(Clone, CloneIsMemcpy, AutoClone)]
struct HasRange {
r: Range<usize>,
} which makes that wrapping struct behave exactly like Copy does today. If the original problem was that we wanted Range to behave like AutoClone in some cases but not when used as an iterator, then ok fair enough my idea doesn't help. |
We've discussed this at recent Libs meetings and considered using an edition to switch the type produced by range ( I think @sfackler had some thoughts on how we could do that. |
some additional benefits are that all fields of the new range types can be public, since they don't need extra hidden fields to track whether the last item has been returned for inclusive ranges. |
I missed this, sorry, but that doesn't address why iterator types aren't The original concern is that something like this could be written: let iter = 0..10;
for i in iter {
if i == 5 { break; }
}
for _ in iter {} And instead of mutating What has been suggested over the years is a lint, and the That is, let iter = 0..10;
for i in iter.clone() {
if i == 5 { break; }
}
for _ in iter {} Whereas just changing the expansion of |
Given the recent developments in rust-lang/rust#84513, would it be possible to apply a similar hack to transition to new range types that don't implement Iterator? |
@Kimundi there was a recent Zulip thread on whether to pursue this for the 2021 edition: https://rust-lang.zulipchat.com/#narrow/stream/268952-edition-2021/topic/range.20reform The gist is that this issue isn't really blocked on how to implement it, it's blocked on what to implement. WRT a new type that implements WRT implementation strategy, that Zulip thread discusses sfackler's draft RFC for this issue, which predates the edition-dependent IntoIterator strategy: https://hackmd.io/cM22mZ5JRH2RAdSART8bWQ?view#Reference-level-explanation . That strategy might be worth taking into consideration for a future version of the RFC, but it doesn't appear to be strictly necessary. Also note that eddyb's MustClone proposal could be implemented without an edition change (although it too makes an ergonomic tradeoff). |
map could be an inherent method. How many of the iterator methods are people actually using on ranges? I'd be surprised if it were much besides map/sum/product. |
- We can now derive Copy trait because RangeInclusive no longer implements Iterator, just IntoIterator. Discussions on not deriving Copy made the argument that it doesn't remain clear when an iterator is mutating the range or not (Iterator and Copy should be mutually exclusive?) see issue: rust-lang/rfcs#2848 - also updated is_empty with the defn found in the std library.
There may be some interest in waking this idea up for edition 2024: rust-lang/lang-team#209 |
There is a pre-RFC proposing to make this change: https://internals.rust-lang.org/t/pre-rfc-fixing-range-by-2027/19936/25 |
From rust-lang/rust#48649 (closed as needing an RFC): It would be nice for
Range<Idx: Copy>
to implementCopy
.I want to store a Range in a struct, but that prevents me from making the struct Copy. I can make a (start, end) struct that wraps it but that seems a little silly.
With
Range
from the standard library (playground):There is more discussion of pros and cons in the rust-lang/rust issue.
The text was updated successfully, but these errors were encountered: