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

[SR-14325] Support Strideable on finite enums. #56684

Closed
gonzalolarralde opened this issue Mar 11, 2021 · 4 comments
Closed

[SR-14325] Support Strideable on finite enums. #56684

gonzalolarralde opened this issue Mar 11, 2021 · 4 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. standard library Area: Standard library umbrella

Comments

@gonzalolarralde
Copy link
Contributor

Previous ID SR-14325
Radar None
Original Reporter @gonzalolarralde
Type Bug
Status Closed
Resolution Won't Do
Additional Detail from JIRA
Votes 0
Component/s Standard Library
Labels Bug
Assignee None
Priority Medium

md5: 3907fc2cf0862c6519998f5ec4e75d65

relates to:

  • SR-2016 stride(from:0 as UInt8, though: 255, by: 2) should not trap.
  • SR-13883 StrideToIterator and StrideThroughIterator are broken

Issue Description:

Current implementation of Stride(To|Through)Iterable relies on passing beyond the upper bound parameter _end in order to detect the completion of the iteration.

Given:

enum E : Int, Strideable {
   case one = 1, two, three, four
   func distance(to other: Self) -> Int {
      return other.rawValue - self.rawValue
   }
   func advanced(by n: Int) -> Self {
      return Self(rawValue: self.rawValue + n)!
   }
}

it is impossible to iterate to or through the last value, as it'd require a non-existant five (or a catch-all-unknown value) to exist in order to detect the completion of the iteration.

It is for that reason that this calls crashes when .three is attempted to be advanced by 2:

print(Array(stride(from: E.one, to: E.four, by: 2)))

The resolution of SR-2016 SR-13883 solves a very similar issue when trying to deal with numeric type's frontiers by relying on FixedWidthInteger's min and max. This ticket is to find a way to extend this further so other types can stridden without requiring to represent otherwise-impossible values.

@gonzalolarralde
Copy link
Contributor Author

Linked similar issues as related. They way they've been addressed on numeric types could help as a reference.

@xwu
Copy link
Collaborator

xwu commented Mar 11, 2021

@gonzalolarralde, as discussed in the PR for SR-2016 and SR-13883, there is no generic way to check the distance at each step without potentially trapping, leading to a behavior regression. Therefore, you must implement this in your own code.by customizing not just distance(to:) and advanced(by:), but also _step(after:from:by:).

See the new documentation comment for an explanation as to how this works, but in brief, you can write something like the following (I'm writing this freehand, so check against the compiler):

extension E {
  static func _step(
    after current: (index: Int?, value: E), from start: E, by distance: Int
  ) -> (index: Int?, value: E) {
    let rawValue = current.value.rawValue + distance
    if rawValue < 1 { return (.min, .one) }
    if rawValue > 4 { return (.min, .four) }
    return (nil, E(rawValue: rawValue))
  } 
}

@gonzalolarralde
Copy link
Contributor Author

I think that's a perfectly valid workaround. Thanks, will close this.

@gonzalolarralde
Copy link
Contributor Author

A workaround is now possible after PR#34860 being merged.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. standard library Area: Standard library umbrella
Projects
None yet
Development

No branches or pull requests

2 participants