Reputation: 497
I'd like to extend the CaseIterable protocol so that all CaseIterable enums have a random
static var that returns a random case. This is the code I've tried
public extension CaseIterable {
static var random<T: CaseIterable>: T {
let allCases = self.allCases
return allCases[Int.random(n: allCases.count)]
}
}
But this fails to compile. Is there a way to achieve this using a static var? Or if not, how would I write the equivalent static func?
ps Int.random extension for anyone playing along at home:
public extension Int {
static func random(n: Int) -> Int {
return Int(arc4random_uniform(UInt32(n)))
}
}
Upvotes: 2
Views: 163
Reputation: 299265
The existing answers are correct about how to fix it, but the why is probably as useful to understand. This is not doing what you think:
public extension CaseIterable {
static var random<T: CaseIterable>: T {
// ...
}
}
This defines a property, random
, which will return a value of type T
, which is any type selected by the caller that conforms to CaseIterable. For example, according to this definition, I can make the following call:
let direction: LayoutDirection = Axis.random
And in this case, Axis
promises that it will return a LayoutDirection
, because that's what the caller has passed as T
. Clearly this isn't what you want. You mean, every CaseIterable can return a random value of its own type, and as the other answer show, you do that with static var random: Self
.
Generic type parameters (like T
in this case) are parameters. They're passed by the caller, not chosen by the implementer.
Upvotes: 2
Reputation: 236260
You can NOT create a generic type on a computed property. There is no need to randomize an index. You can use Collection
's randomElement method to return a random case and return Self
:
public extension CaseIterable {
static var random: Self { allCases.randomElement()! }
}
Playground testing:
enum Test: String, CaseIterable {
case a, b, c, d, e
}
let test: Test = .random // d
If you would like to return a random value you can constrain Self
to RawRepresentable
and return RawValue
:
public extension CaseIterable where Self: RawRepresentable {
static var randomValue: RawValue { allCases.randomElement()!.rawValue }
}
enum Test: String, CaseIterable {
case a, b, c, d, e
}
let test = Test.randomValue // "c"
Upvotes: 2
Reputation: 54426
You can actually use randomElement
directly in the CaseIterable
extension:
public extension CaseIterable {
static var random: Self {
allCases.randomElement()!
}
}
Upvotes: 2