Random cannot be used on instance Int, behaviour on Linux

Viewed 62

I am trying to figure out how to make the below code compile on linux. The solution for Darwin self.init(arc4random_uniform(UInt32(upperLimit))) works well, but self.init(random() % upperLimit) generates the error below.

extension Int {
    /// Initializes a new `Int ` instance with a random value below a given `Int`.
    ///
    /// - Parameters:
    ///   - randomBelow: The upper bound value to create a random value with.
    public init?(randomBelow upperLimit: Int) {
        guard upperLimit > 0 else { return nil }
        #if os(Linux)
            self.init(random() % upperLimit)
        #else
            self.init(arc4random_uniform(UInt32(upperLimit)))
        #endif
    }
}

Error:

error: repl.swift:11:27: error: static member 'random' cannot be used on instance of type 'Int'
                self.init(random() % upperLimit)
                          ^~~~~~
                          Self.
  • I have a hunch this can be fixed with getters, but I wasn't able to find the solution. Any help is appreciated.
1 Answers

If you're receiving this error, then presumably you're using Swift 4.2 that comes with a new random API. The error is arising because the compiler thinks you're trying to refer to the new static random(in:) method on FixedWidthInteger, which Int conforms to (the fact that it ignores the global function is a known bug).

To generate a random Int from 0 up to (but not including) a given upper bound n, you can now just say:

let i = Int.random(in: 0 ..< n)

See the above linked proposal for more info on the new random API.

Given how simple the new API is, I'd argue that there's no need to implement a custom
init?(randomBelow:). However, if still want to, you can implement it like so:

extension Int {
  /// Initializes a new `Int ` instance with a random value below a given `Int`.
  ///
  /// - Parameters:
  ///   - randomBelow: The upper bound value to create a random value with.
  public init?(randomBelow upperLimit: Int) {
    guard upperLimit > 0 else { return nil }
    self = .random(in: 0 ..< upperLimit)
  }
}

The new random API is cross-platform, so there's now no need to do any conditional compilation.

Related