-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Tracking issue for step_trait
stabilization
#42168
Comments
…excrichton Give step_trait a distinct tracking issue from step_by iterator_step_by has decoupled their futures, so the tracking issue should split. Old issue: rust-lang#27741 New issue: rust-lang#42168 r? @alexcrichton (another follow-up to closed PR rust-lang#42110 (comment))
…excrichton Give step_trait a distinct tracking issue from step_by iterator_step_by has decoupled their futures, so the tracking issue should split. Old issue: rust-lang#27741 New issue: rust-lang#42168 r? @alexcrichton (another follow-up to closed PR rust-lang#42110 (comment))
…excrichton Give step_trait a distinct tracking issue from step_by iterator_step_by has decoupled their futures, so the tracking issue should split. Old issue: rust-lang#27741 New issue: rust-lang#42168 r? @alexcrichton (another follow-up to closed PR rust-lang#42110 (comment))
…excrichton Give step_trait a distinct tracking issue from step_by iterator_step_by has decoupled their futures, so the tracking issue should split. Old issue: rust-lang#27741 New issue: rust-lang#42168 r? @alexcrichton (another follow-up to closed PR rust-lang#42110 (comment))
…excrichton Give step_trait a distinct tracking issue from step_by iterator_step_by has decoupled their futures, so the tracking issue should split. Old issue: rust-lang#27741 New issue: rust-lang#42168 r? @alexcrichton (another follow-up to closed PR rust-lang#42110 (comment))
…excrichton Give step_trait a distinct tracking issue from step_by iterator_step_by has decoupled their futures, so the tracking issue should split. Old issue: rust-lang#27741 New issue: rust-lang#42168 r? @alexcrichton (another follow-up to closed PR rust-lang#42110 (comment))
Some progress on this in #42534 |
PR #43077 does items 1 |
I’ve removed the i128/u128 stuff from my PR because I suspect my mixed-signdeness mixed-width interger arithmetic was buggy. I also massaged the /// Supporting trait for allowing ranges of various different types to be iterators.
#[unstable(feature = "step_trait",
reason = "recently redesigned",
issue = "42168")]
pub trait Step: Clone + PartialOrd + Sized {
/// Returns the number of steps between two step objects. The count is
/// inclusive of `start` and exclusive of `end`.
///
/// Returns `None` if it is not possible to calculate `steps_between`
/// without overflow.
fn steps_between(start: &Self, end: &Self) -> Option<usize>;
/// “Go forward” (for integers, add) the given number of steps, returning None on overflow.
fn forward(&self, step_count: usize) -> Option<Self>;
/// “Go backward” (for integers, subtract) the given number of steps, returning None on overflow.
fn backward(&self, step_count: usize) -> Option<Self>;
/// Modify the given inclusive range so that it becomes empty,
/// for example by setting it to `1...0`.
fn make_inclusive_range_empty(range: &mut ops::RangeInclusive<Self>);
} How does it look? I’m a bit uncertain of the exact impls for integers, there is up to 6 cases to consider: {smaller, same width, larger} than usize/isize × {signed, unsigned}. |
I’ve tweaked this design some more and opened #43127. I believe that PR fixes every issue I know of around the |
I’ve closed that PR since it needs more work that I’m not planning to do soon: #43127 (comment). Hopefully someone else can take over. |
I just wanted to leave a note saying that if methods with the behavior of Having said that, it sounds like these methods are scheduled to be either removed or replaced with something more generally useful? Update: (maybe if I care about this, I should spend effort reviving PR #43127...) |
Follow up: per this Stack Overflow thread, it might be worth coming up with new names for |
Clarify Step Documentation While the redesign is in progress (rust-lang#62886), clarify the purpose of replace_zero and replace_one. First, "returning itself" is technically impossible due to the function signature of &mut self -> Self. A clone or copy operation must be used. So this is now explicitly stated in the documentation. Second, the added docs give some guidance about the actual contract around implementation of replace_zero and replace one. Specifically, the only usage is to create a range with no more steps, by setting start to replace_one and end to replace_zero. So the only property that is actually used is `replace_one > replace_zero`. See rust-lang#42168 (comment) The new documentation does not say that is the *only* contract, and so it should not be considered an api change. It just highlights the most important detail for implementors. The redesign doesn't seem to be landing any time soon, so this is a stopgap measure to reduce confusion in the meantime.
Consider a newtype |
Edit Please ignore below. My brain was clearly not working properly. I only keep this message in the interest of transparency. Original
That still doesn't make sense to me. If you want it to be more flexible, then there shouldn't be a To me this is like having Or to match your contrived type with an even more contrived type, imagine I have a type whose canonical order defined on it via The point being it should either require |
You don't want to be so flexible as to say there's no ordering at all, though. Take As for your example, if you have |
Edit Please ignore below. My brain was clearly not working properly. I only keep this message in the interest of transparency; although the claim that the trait would be more generally useful without the Original Why not? A successor function need not rely on an existing ordering implementation to work. If one insists on coupling the order that is implied by the successor function to be the same as any existing order via Or again, one should require |
I feel that an implementation might want to express that an ordering between two “uncomparable” things has limited meaning even if it can mathematically be constructed from the successor relation. I also think there are two “styles” of hierarchy at conflict here. Mathematically we want to say that |
Edit Please ignore below. My brain was clearly not working properly. I only keep this message in the interest of transparency; although the claim that the trait would be more generally useful without the Original If one implemented The first question is whether or not one wants Addendum A consumer of Even if one wants to require the enumeration to be in agreement, you can offer an "out" by not making If you do want to force developers to write more code, however, by forcing a supertrait as well as reduce the generality of |
It seems to me that it is possible to have a |
Although I agree that just leaving out any |
Edit Please ignore below. My brain was clearly not working properly. I only keep this message in the interest of transparency. Original I haven't constructed a formal FOL proof yet, but I claim the following is valid: #![feature(step_trait)]
impl<T: core::iter::Step> Ord for T {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.partial_cmp(other).unwrap()
}
} |
Edit Please ignore below. My brain was clearly not working properly. I only keep this message in the interest of transparency; although the claim that the trait would be more generally useful without the Original
Exactly. I am not necessarily arguing for However that does require proof that for any type |
I can tell you, for a fact, this will panic. E.g. #[derive(Clone, Copy, PartialEq, Eq)]
enum EvenOrOdd {
Even(u8),
Odd(u8)
}
impl EvenOrOdd {
fn map(self, f: impl FnOnce(u8) -> Option<u8>) -> Option<Self> {
match self {
Even(x) => f(x).map(Even),
Odd(x) => f(x).map(Odd)
}
}
}
fn matching_evenness(x: EvenOrOdd, y: EvenOrOdd) -> Option<(u8, u8)> {
match (x, y) {
(Even(l), Even(r)) => Some((l, r)),
(Odd(l), Odd(r)) => Some((l, r)),
_ => None
}
impl PartialOrd<Self> for EvenOrOdd {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
matching_evenness(*self, *rhs).and_then(|(x, y)| x.partial_cmp(&y))
}
}
impl Step for EvenOrOdd {
fn steps_between(start: &Self, other: &Self) -> Option<usize> {
matching_evenness(*start, *other).and_then(|(x, y)| y.checked_sub(x).map(|diff| (diff / 2) as usize))
}
fn forward_checked(start: Self, count: usize) -> Option<Self> {
start.map(|x| {
u8::try_from(count * 2).ok()
.and_then(|y| x.checked_add(y))
})
}
fn backward_checked(start: Self, count: usize) -> Option<Self> {
start.map(|x| {
u8::try_from(count * 2).ok()
.and_then(|y| x.checked_sub(y))
})
}
} |
Edit Please ignore below. My brain was clearly not working properly. I only keep this message in the interest of transparency. Original It's a lot nicer if you give code that can compile. Additionally you haven't proven you conform to the requirements mentioned by |
Sorry, writing on my phone so I left the |
Edit Please ignore below. My brain was clearly not working properly. I only keep this message in the interest of transparency. Original You also have misplaced Here is code that compiles that needs to have #![feature(step_trait)]
use core::cmp::Ordering;
use core::iter::Step;
fn main() {
partial_cmp_proof();
step_proof();
counterexample();
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum EvenOrOdd {
Even(u8),
Odd(u8),
}
use EvenOrOdd::{Even, Odd};
impl EvenOrOdd {
fn map(self, f: impl FnOnce(u8) -> Option<u8>) -> Option<Self> {
match self {
Even(x) => f(x).map(Even),
Odd(x) => f(x).map(Odd),
}
}
fn ord_impl(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
fn matching_evenness(x: EvenOrOdd, y: EvenOrOdd) -> Option<(u8, u8)> {
match (x, y) {
(Even(l), Even(r)) => Some((l, r)),
(Odd(l), Odd(r)) => Some((l, r)),
_ => None,
}
}
impl PartialOrd<Self> for EvenOrOdd {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
matching_evenness(*self, *rhs).and_then(|(x, y)| x.partial_cmp(&y))
}
}
impl Step for EvenOrOdd {
fn steps_between(start: &Self, other: &Self) -> Option<usize> {
matching_evenness(*start, *other)
.and_then(|(x, y)| y.checked_sub(x).map(|diff| (diff / 2) as usize))
}
fn forward_checked(start: Self, count: usize) -> Option<Self> {
start.map(|x| u8::try_from(count * 2).ok().and_then(|y| x.checked_add(y)))
}
fn backward_checked(start: Self, count: usize) -> Option<Self> {
start.map(|x| u8::try_from(count * 2).ok().and_then(|y| x.checked_sub(y)))
}
}
fn partial_cmp_proof() {
// Code to be written.
}
fn step_proof() {
// Code to be written.
}
fn counterexample() {
// Code to be written.
} |
Well, the panic is easy, As for the invariants
|
Edit Please ignore below. My brain was clearly not working properly. I only keep this message in the interest of transparency. Original You'll have to forgive my ignorance, but I don't find it obvious that For example
Maybe I just need to eat, and it'll be obvious to me. |
Jesus, I am an idiot and am thinking way too hard about this. I clearly missed the forest for the trees here. Your example is extremely obvious and highlights a massive oversight on my part about what Sorry @khoover for wasting your time and likely frustrating you with my stupidity. |
Yep, any implementation is going to induce a partial order via fn partial_cmp<T: Step>(this: &T, other: &T) -> Option<Ordering> {
T::steps_between(this, other)
.map(|n| if n == 0 { Equal } else { Less })
.or_else(|| T::steps_between(other, self).and(Some(Greater)))
// That and works because Step guarantees T::steps_between(other, this) == Some(0)
// iff T::steps_between(this, other) == Some(0), which was already covered in the above map.
} EDIT: and the important thing is that you can't do any better than a partial order. |
Indeed; although that still doesn't "prove" that The documentation could be made a little clearer as well. For example, "For any |
No, no, it should be for every Or, to make it more explicit, |
I just need to shut up, lol. The original comment before I edited it also stated that " Thanks again for holding my hand. |
I would like to note an interesting detail about |
I think the more likely approach is to say that any different elements of your modular ring are incomparable, i.e. ^1: Well, by the trait's contract, you have to output |
Please mark the |
There're mistakes in
So the line should actually be:
The same goes for its counterpart under |
Split off from #27741 because the stabilization path for
step_by
has moved to being on iterators (#41439), and thus not using theStep
trait.step
,steps_between
, andis_negative
once Range::step_by is deletedreplace_zero
andreplace_one
with something more useful (some options: Make RangeInclusive just a two-field struct (amend 1192) rfcs#1980 (comment))steps_between_by_one
so thatRange<u128>
can beTrustedLen
(rather than it only working well with types that fit inusize
)steps_between
should work Step::steps_between does not distinguish overflow and unimplemented (unstable) #48117(and probably more)
The text was updated successfully, but these errors were encountered: