Why isn't the `count` parameter of type `UIInt` in `String.init(repeating:count:)`?

Although the swift documentation says that developers should "avoid" using UIInt,

it also says "Swift is a type safe language and encourages you to be clear about the types of values your code can work with.".


So why isn't the `count` parameter of type `UIInt` in `String.init(repeating:count:)`?


Note: It crashes if you pass a negative number

Accepted Reply

But I am still wondering why for that specific method for example, the decision was to crash if a negative number is passed instead of protecting the method call by making it not to accept invalid inputs using

UInt
.

Because once you start adding unsigned in one place it starts to spread through your code like a plague (-: For example, imagine you have an array of strings

a
and you want to create a ‘clear’ copy, where each element in the empty string. You might write this:
let a: [String] = ["Hello", "Cruel", "World!"]
var b = [String](repeating: "", count: a.count)

If the

count
parameter to
init(repeating:count:)
were
UInt
, then the
count
property on the array would have to be
UInt
as well.

Now imagine

b
is used to populate the rows in a
UITableView
. For example:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return b.count
}

Now

UITableView
has to use
UInt
to represents rows and row counts. However,
UITableView
makes extensive use of
IndexPath
, meaning that
IndexPath
has to use
UInt
as well. And so on.

Having gone down this path in C I’m very happy with the change of direction that Swift has taken.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

It's a kind of philosophical decision. In Swift, the various integer types are all fully type safe, and there's no auto-converting from one to another. (You have to explicitly convert instead.) That means mixing types is painful. The easiest solution, or at least the one Swift took, is to standardize on Int as the "normal" integer type. If you stick with Int everywhere, then you don't have to load down the code with conversions. Range restrictions (such as "must be >= 0") are then implemented as API preconditions.


You run into the same kind of issue often with C# on Windows. If you use UInt for values that cannot be negative, you start to run into conversion annoyances when you try to index into arrays and lists, and it's often just easier to use Int — the type that's already used by the APIs you want.


It's also worth noting that Cocoa generally uses NSUInteger as its "standard" type in Obj-C, except when it doesn't. (Table views, for example, uses NSInteger for row indexes, so that they can return -1 for a not-found row result.) Both NSInteger and NSUInteger bridge to Int in Swift by default, which is counter-intuitive but preferable.

>It crashes if you pass a negative number


Which version Swift does that/are you using?


From the Swift 4.2 change log...


"The C

size_t
family of types are now imported into Swift as
Int
, since Swift prefers sizes and counts to be represented as signed numbers, even if they are non-negative."

There’s also a historical perspective on this. C originated in a world where

int
was 16 bits, so the difference in range between signed and unsigned was really important. Swift originated in a world where
int
was 32 bits, where that difference is much less relevant. Moreover, the Swift
Int
type is pointer sized, so for the majority of its platforms
Int
is actually 64-bits, where the range you lose by ignoring signed versus unsigned is quite irrelevant.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Ok, it makes sense.

But I am still wondering why for that specific method for example, the decision was to crash if a negative number is passed instead of protecting the method call by making it not to accept invalid inputs using UIInt.

But I am still wondering why for that specific method for example, the decision was to crash if a negative number is passed instead of protecting the method call by making it not to accept invalid inputs using

UInt
.

Because once you start adding unsigned in one place it starts to spread through your code like a plague (-: For example, imagine you have an array of strings

a
and you want to create a ‘clear’ copy, where each element in the empty string. You might write this:
let a: [String] = ["Hello", "Cruel", "World!"]
var b = [String](repeating: "", count: a.count)

If the

count
parameter to
init(repeating:count:)
were
UInt
, then the
count
property on the array would have to be
UInt
as well.

Now imagine

b
is used to populate the rows in a
UITableView
. For example:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return b.count
}

Now

UITableView
has to use
UInt
to represents rows and row counts. However,
UITableView
makes extensive use of
IndexPath
, meaning that
IndexPath
has to use
UInt
as well. And so on.

Having gone down this path in C I’m very happy with the change of direction that Swift has taken.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Great answer. Thank you so much for clarifying this