Подтвердить что ты не робот

В Swift, как мне избежать как опций, так и нулевых ссылок на объекты?

Вся причина для опций заключается в предотвращении сбоев во время выполнения, вызванных ударом переменной, назначенной nil/null/none. Следовательно, переменные не могут быть nil; вместо этого они могут быть завернуты в необязательный тип, который представляет их как Some или None, и разворачивается для получения определенного содержимого Some или nil.

Но если вы разворачиваете их повсюду с помощью ! или с помощью неявно освобожденных опций, вы просто вводите эту возможность, так как вы несовершенный кодер, из-за сбоев во время выполнения. Если вы используете if let, чтобы безопасно разворачивать их, вы избегаете сбоев, но вы застреваете внутри области действия оператора if let для работы со значением внутри Some, и вам все равно придется обрабатывать потенциальный случай nil. Если вы используете? для временного разворачивания вызова метода, он будет завершен после завершения, введя беспорядочную возможность многоуровневой дополнительной упаковки.

Итак: Мне кажется, что нужно сделать это, избегая опций, кроме случаев, когда это необходимо (например, при вызове методов инфраструктуры, которые возвращают их). Но если я не использую опции, это означает, что мои ссылки на объекты должны быть не равными нулю, и я не могу понять, как обрабатывать случаи, когда по той или иной причине не должно быть или нет, значение, присвоенное объектной ссылке.

Мой вопрос: как мне избежать необходимости в нуле? Похоже, для этого требуется другой подход к программированию. (Или я должен использовать только опции, и если это то, что я делаю, как это лучше, чем просто иметь нулевые назначения для ссылок на объекты, например, на других языках?)

Я знаю, что это потенциально субъективный вопрос, но где еще я должен его спрашивать? Я не пытаюсь троллить или возбуждать дебаты, мне бы очень хотелось узнать, какой правильный подход, поскольку я пишу более быстрый код.

4b9b3361

Ответ 1

Вы правы, опционы могут быть болью, поэтому вы не должны злоупотреблять ими. Но они больше, чем просто то, с чем вам приходится иметь дело при использовании фреймворков. Они являются решением невероятно распространенной проблемы: как обрабатывать вызов, который возвращает результат, который может быть в порядке, или может не быть.

Например, возьмите элемент Array.first. Это удобная утилита, которая дает вам первый элемент массива. Почему полезно иметь возможность называть a.first, когда вы можете просто позвонить a[0]? Потому что во время выполнения массив может быть пустым, и в этом случае a[0] взорвется. Конечно, вы можете проверить a.count заранее, но затем снова

а. вы можете забыть,

и

б. что приводит к довольно уродливому коду.

Array.first справляется с этим, возвращая необязательный. Поэтому вам необходимо развернуть необязательный параметр, прежде чем вы сможете использовать значение, являющееся первым элементом массива.

Теперь, к вашей проблеме об развернутом значении, которое существует только внутри блока с помощью if let. Представьте себе параллельный код, проверяя количество массивов. Это будет одно и то же, верно?

if a.count > 0 {
    // use a[0]
}
// outside the block, no guarantee
// a[0] is valid

if let firstElement = a.first {
    // use firstElement
}
// outside the block, you _can't_
// use firstElement

Конечно, вы могли бы сделать что-то вроде выполнения раннего возврата из функции, если счетчик равен нулю. Это работает, но немного подвержено ошибкам - что, если вы забыли сделать это или положить его в условное утверждение, которое не выполнялось? По существу, вы можете сделать то же самое с Array.first: сначала проверьте счетчик функции, а затем выполните array.first!. Но этот ! как сигнал для вас - будьте осторожны, вы делаете что-то опасное, и вам будет жаль, если ваш код не совсем корректен.

Опционы также помогают сделать альтернативы немного красивее. Предположим, вы хотели установить значение по умолчанию, если массив пуст. Вместо этого:

array.count > 0 ? a[0] : somedefault

вы можете написать это:

array.first ?? somedefault

Это лучше по нескольким причинам. Это ставит важную вещь впереди: значение, которое вы хотите, это то, как начинается выражение, за которым следует значение по умолчанию. В отличие от тернарного выражения, которое сначала показывает вам выражение проверки, а затем значение, которое вы действительно хотите, а затем, наконец, значение по умолчанию. Это также более безопасно - гораздо проще избежать опечатки и невозможно, чтобы эта опечатка привела к взрыву во время работы.

Возьмем другой пример: функция find. Это проверяет, находится ли значение в коллекции и возвращает индекс его позиции. Но ценность может не присутствовать в коллекции. Другие языки могут справиться с этим, возвращая индекс конца (который не указывает на значение, а скорее минус последнее значение). Это то, что называется "дозорным" значением - значение, которое выглядит как обычный результат, но на самом деле имеет особое значение. Как и в предыдущем примере, вам нужно проверить, что результат не был равен индексу end перед его использованием. Но вы должны это знать. Вы должны найти документы для find и подтвердить, как это работает.

Если find возвращает необязательный, это просто естественно, когда вы понимаете необязательную идиому, чтобы понять, что причина в том, что результат может быть недействительным по очевидной причине. И все же о безопасности, упомянутой выше, также применяются - вы случайно не можете забыть и использовать результат в качестве индекса, потому что сначала вам нужно развернуть его.

Тем не менее, вы можете использовать опциональные опции, поскольку они требуют бремени для проверки. Вот почему индексы массива не возвращают необязательные варианты - это создало бы так много хлопот, чтобы постоянно их проверять и разворачивать, особенно если вы знаете, что используемый вами индекс действителен (например, вы находитесь в цикл for над допустимым диапазоном индексов массива), что люди будут постоянно использовать ! и, таким образом, загромождают свой код без выгоды. Но затем вспомогательные методы, такие как первый и последний, добавляются, чтобы охватить распространенные случаи, когда люди хотят быстро выполнить операцию, не проверяя сначала размер массива, но хотите сделать это безопасно.

(Сдвиговые словари, с другой стороны, должны регулярно получать доступ с помощью недопустимых индексов, поэтому их метод [key] возвращает необязательный)

Еще лучше, если вообще можно избежать возможности выхода из строя. Например, когда filter не соответствует никаким элементам, он не возвращает nil необязательный. Он возвращает пустой массив. "Ну, очевидно, это будет", можно сказать. Но вы удивляетесь, как часто вы видите, что кто-то делает возвращаемое значение, например массив, когда на самом деле они просто должны возвращать пустую. Поэтому вы совершенно правильно говорите, что вам следует избегать опций, за исключением случаев, когда они необходимы - это просто вопрос о том, какие необходимые средства. В приведенных выше примерах Id говорят, что они необходимы, и лучшее решение альтернатив.

Ответ 2

Или я должен использовать только опции, и если это то, что я делаю, как это лучше, чем просто иметь нулевые назначения объекту ссылки на другие языки?

Если вашему вычислению может потребоваться вернуть "специальное" значение, то да, в Swift вы должны использовать дополнительные опции. Они лучше типов с нулевым значением, потому что они явны. Легко пропустить случай, когда указатель может быть nil, его намного сложнее (но вполне возможно), чтобы прикрутить опциональные опции.

Если вы используете "if let", чтобы безопасно разворачивать их, вы избегаете сбоев, но вы застряли в объеме инструкции "если да", чтобы работать с значение, и вам все равно придется обрабатывать потенциальный случай nil.

Это особенность. Я имею в виду, что это целая точка необязательного типа: вы должны обрабатывать оба случая (nil и не nil), и вы должны быть явно об этом.

См. Haskells Возможно, тип и монада для примера аналогичной концепции. Тип Maybe - это точный эквивалент необязательных типов, возможно, монада упрощает "цепочку" операций с этими необязательными значениями без необходимости вручную проверять пустые значения все время.