Почему руководство по языку Swift предлагает использовать Int "даже когда значения, как известно, являются неотрицательными"?
Это вопрос о стиле программирования в Swift, в частности Int
vs UInt
.
Руководство по языку Swift Programming Language советует программистам использовать общий целочисленный тип со знаком Int
, даже если переменные известны как неотрицательные. Из руководства:
Использовать UInt только тогда, когда вам определенно нужен целочисленный тип без знака с тем же размером, что и размер родного слова платформы. Если это не так, Int является предпочтительным, даже если значения, которые необходимо сохранить, являются неотрицательными. Согласованное использование Int для целочисленных значений помогает взаимодействовать с кодами, избегает необходимости конвертировать между различными типами номеров и соответствует выводу целочисленного типа, как описано в разделе "Безопасность типа и тип".
Однако UInt
будет 32-разрядным без знака в 32-разрядных архитектурах и 64-разрядных беззнаковых в 64-разрядных архитектурах, поэтому нет преимущества для производительности при использовании Int
over UInt
.
Напротив, руководство Swift дает более поздний пример:
пусть возраст = -3
assert (age >= 0, "возраст человека не может быть меньше нуля" )
//это вызывает утверждение, вызываемое, потому что возраст не >= 0
Здесь проблема времени выполнения может быть обнаружена во время компиляции, если код был написан как:
let age:UInt = -3
// this causes a compiler error because -3 is negative
Есть много других случаев (например, что-либо, что будет индексировать коллекцию), где использование UInt
будет ловить проблемы во время компиляции, а не во время выполнения.
Итак, вопрос: является ли совет в звуке руководства Swift Programming Language и полезны при использовании Int
", даже если значения, которые должны быть сохранены, являются неотрицательными" перевешивают преимущества безопасности использования UInt
?
Дополнительная заметка: Используя Swift в течение нескольких недель, теперь ясно, что для взаимодействия с Cocoa UInt
требуется. Например, инфраструктура AVFoundation
использует целые числа без знака, где требуется "подсчет" (количество выборок/кадров/каналов и т.д.). Преобразование этих значений в Int
может привести к серьезным ошибкам, когда значения больше, чем Int.max
Ответы
Ответ 1
Я не думаю, что использование UInt безопасно, как вы думаете. Как вы отметили:
let age:UInt = -3
приводит к ошибке компилятора. Я также пробовал:
let myAge:Int = 1
let age:UInt = UInt(myAge) - 3
что также привело к ошибке компилятора. Однако следующие (по моему мнению, гораздо более распространенные в реальных программах) сценарии не имели ошибки компилятора, но фактически приводили к ошибкам времени выполнения EXC_BAD_INSTRUCTION
:
func sub10(num: Int) -> UInt {
return UInt(num - 10) //Runtime error when num < 10
}
sub10(4)
а также:
class A {
var aboveZero:UInt
init() { aboveZero = 1 }
}
let a = A()
a.aboveZero = a.aboveZero - 10 //Runtime error
Если бы это было просто Int
s, вместо сбоя вы могли бы добавить код для проверки своих условий:
if a.aboveZero > 0 {
//Do your thing
} else {
//Handle bad data
}
Я бы даже зашел так далеко, чтобы приравнять их советы против использования UInt
к их совету против использования неявно развернутых опций: не делайте этого, если вы не уверены, что не получите никаких негативов, потому что в противном случае вы получить ошибки во время выполнения (за исключением простейших случаев).
Ответ 2
Он говорит в вашем вопросе..
"Последовательное использование Int для целочисленных значений помогает взаимодействовать с кодами, избегает необходимости конвертировать между различными типами номеров и соответствует выходу типа целочисленного типа, как описано в разделе" Безопасность типа и тип ввода ".
Это позволяет избежать таких проблем, как назначение Int для UInt. Отрицательные значения Int, назначенные UInts, приводят к большим значениям вместо предполагаемого отрицательного значения. Бинарное представление обоих не отличает один тип от другого.
Кроме того, оба являются классами, один из которых не происходит от другого. Полученные классы, получающие Ints, не могут получать UInts без перегрузки, что означает, что преобразование между ними будет общей задачей UInts, когда большая часть фреймов принимает Ints. Преобразование между ними может стать и не-тривиальной задачей.
В двух предыдущих параграфах говорится о "интероперабельности" и "преобразовании между разными типами номеров". Проблемы, которые следует избегать, если UInts не используются.