Swift vs Rust - обзор Swift с Rust точки зрения
Перевод | Автор оригинала: Rhymu
Я впервые изучил Swift в 2017 году, что для меня было много лет назад. Тогда это была версия 3.1. Я гораздо лучше знаком с Rust, изучаю и использую его уже около 8 месяцев. Поэтому, когда я вернулся, чтобы снова проверить Swift на этой неделе, я был действительно удивлен тремя вещами:
- Swift теперь версии 5.3 (две основные версии!)
- Swift наконец-то перенесен на Windows!
- Ого, код Swift очень похож на код Rust!
Я подумал, что было бы забавно сесть и делать заметки, читая официальную книгу Swift Book, чтобы «освежить» язык. Будучи очень увлеченным Rust, я подумал, что было бы интересно включить в свои заметки любые сравнения, которые я мог бы провести между Swift и Rust. Это заняло несколько дней, и к концу у меня было записано довольно много заметок. Поэтому я решил немного их очистить и выложить здесь!
Ниже приводится краткое сравнение Swift (версия 5.3) и Rust (версия 2018). Он следует за главами официальной книги Swift. Swift и Rust очень похожи, поэтому в целом, если здесь не упоминается конкретная точка сравнения, возможно, два языка совпадают в этом отношении или «достаточно близки», что, по моему мнению, это не имеет значения. . Например, значения true и false одинаковы для Swift и Rust, а синтаксис для различных типов строковых литералов в основном одинаков, поэтому я не стал их записывать. С другой стороны, то, что здесь не указано, могло быть тем, о чем я не думал или не знал в то время, и в этом случае, пожалуйста, дайте мне знать в комментариях. Заранее спасибо!
Если какая-либо информация ниже не полностью проверена или протестирована, вопросительный знак? будет использоваться пунктуация. Это открытое приглашение для всех, кто может проверить информацию (правильную или неправильную) и тем или иным образом предоставить доказательства.
Во-первых, я нашел несколько интересных тем общего характера, которые не были охвачены непосредственно Swift Book. Я сгруппировал их вместе, чтобы перечислить здесь:
- Swift Package Manager - аналог Cargo в Rust, являющийся стандартной системой управления пакетами для языка.
- Обратите внимание, что, к сожалению, на момент написания этой статьи Swift Package Manager недоступен для Windows, поэтому вместо этого для создания систем сборки для проектов Swift в Windows часто используются специальные решения, основанные на таких инструментах, как CMake и Ninja.
- Стандартная библиотека Swift - тесно связана с компилятором и облегчает выполнение различных типов и функций, необходимых для различных аспектов языка. Он также обеспечивает поддержку функций, не описанных в книге:
- Сериализация - набор протоколов, аналогичных serde:
- Возможность кодирования - serde::Serialize
- Декодируемый - serde::Deserialize
- Кодировщик - serde::Сериализатор
- Декодер - serde::Deserializer
- Взаимодействие с C - различные типы и функции, аналогичные интерфейсу внешних функций (FFI) в Rust.
- Сериализация - набор протоколов, аналогичных serde:
- Foundation - эта библиотека происходит из экосистемы Apple (iOS, macOS и т.д.) И обычно поставляется в комплекте со Swift из-за предоставления доступа к основным службам операционной системы, которые в Rust обычно либо встроены в стандартную библиотеку Rust, либо доступны из реестр крэйтов сообщества Rust:
- Дата и время управления
- Локализация (форматирование строковых представлений с учетом локали)
- Файловая система - создание, чтение, запись или проверка файлов и каталогов.
- Кодирование / декодирование JSON
- Процессы и потоки
- Сеть
- Диспетчерская (также известная как «Grand Central Dispatch» (GCD) - эта библиотека происходит из экосистемы Apple (iOS, macOS и т.д.) И обычно поставляется в комплекте со Swift из-за предоставления возможностей для асинхронного программирования, аналогичных Futures / async в Rust вместе с futures-rs и средами выполнения, такими как tokio, async-std и smol, доступны в реестре крэйтов сообщества Rust.
TL;DR - Общие различия
Я написал здесь довольно много заметок, возможно, слишком много для некоторых людей. Итак, вот краткое изложение того, «что важно» при сравнении Swift и Rust:
- Swift широко подразделяет типы на «значения» и «ссылочные», скрывая от программиста большую часть деталей реализации ссылок. Rust делает ссылки первоклассным типом и открывает программисту всю систему типов, хорошо это или плохо.
- Swift поддерживает наследование классов, а Rust - нет.
- Swift использует генерирование и перехват исключений для обработки ошибок, в то время как Rust поощряет использование перечисляемых типов для возврата вариантов «успешный или неудачный» из ошибочных операций.
- Большинство конструкций кода в Rust являются выражениями, тогда как в Swift существует более традиционное разделение между «оператором» и «выражением».
- В отличие от Rust, Swift предлагает полный набор функций "свойств" (т.е. вычисляемые свойства, наблюдатели свойств, прогнозируемые значения), аналогичный C#.
- Многие языковые функции, которые имеют свои собственные ключевые слова и синтаксис в Swift, обрабатываются через систему типов Rust (т.е. необязательные типы, упаковка, ссылки, перегрузка операторов).
- Rust включает прямой синтаксис для асинхронного программирования (async, await), а Swift - нет, полагаясь на средства вспомогательной библиотеки для его поддержки.
Раздел мнений
При написании этого поста я старался оставаться максимально объективным. Однако во время этого процесса я почувствовал несколько субъективных вещей. Переходите к следующему разделу, если вас не устраивают предвзятые мнения.
- Оба языка призваны быть языками системного программирования общего назначения. Однако Swift имеет прочные корни в Apple и поэтому страдает двумя основными недостатками:
- Хотя Swift имеет сильную корпоративную поддержку (Apple), его общее сообщество слабее по сравнению с Rust. Будучи относительно необременительным, Rust имеет более "всестороннюю" основу и надежное сообщество, о чем свидетельствуют:
- Более широкий спектр целевых платформ, которые его поддерживают.
- Более простая процедура получения, настройки и использования его инструментальной цепочки.
- Более богатая онлайн-среда для публикации и повторного использования библиотек.
- В то время как Rust включает более продвинутые функции, такие как многопоточность, сетевое взаимодействие и асинхронное кодирование, непосредственно в своей стандартной библиотеке, Swift использует дополнительные библиотеки, такие как Foundation и GCD, которые были разработаны специально для экосистемы Apple. Поддержка других платформ влечет за собой «портирование» этих библиотек постфактум или риск «испортить» язык для платформ, которые не поддерживают библиотеки Apple.
- Хотя Swift имеет сильную корпоративную поддержку (Apple), его общее сообщество слабее по сравнению с Rust. Будучи относительно необременительным, Rust имеет более "всестороннюю" основу и надежное сообщество, о чем свидетельствуют:
- И Rust, и Swift стремятся быть «безопасными», имея в виду дизайн языка и функции компилятора, которые предотвращают и/или обнаруживают ошибки во время компиляции. Однако у них принципиально разные подходы:
- Rust вложился в подход «проверки заимствований», в котором ссылки являются первоклассными типами, а концепция «времени жизни» поднимается до сознательного уровня программиста.
- Стоимость: программисту труднее учиться из-за нетрадиционного подхода и более сложного синтаксиса, требующего больше практики и различных способов мышления и разработки кода.
- Выигрыш: более быстрый и эффективный код, который может использовать как стек, так и кучу, позволяя создавать более сложные конструкции и оптимизации с нулевым копированием.
- Swift использовал традиционный подход «передача по копии или ссылке», при котором ссылки по своей сути подсчитываются.
- Усиление: очень похоже на традиционные языки программирования, с семантикой, уже знакомой большинству программистов, что упрощает изучение и использование языка.
- Стоимость: поскольку используется более традиционный подход, остаются многие из более традиционных недостатков, такие как ненужное копирование и косвенное обращение; хотя, возможно, у компилятора есть возможности оптимизировать по крайней мере некоторые из них.
- Rust вложился в подход «проверки заимствований», в котором ссылки являются первоклассными типами, а концепция «времени жизни» поднимается до сознательного уровня программиста.
- В области объектно-ориентированного программирования Rust придерживается жесткого подхода, поощряя принцип компоновки над наследованием, не поддерживая наследование вообще. Хотя Swift, поддерживающий «протоколы» (трэйты, интерфейсы), делает язык более «дружественным к композиции», он также включает наследование классов. Это делает Swift более удобным для программистов, привыкших к традиционным методам объектно-ориентированного программирования с использованием наследования за счет сложных правил для таких вещей, как инициализаторы и контроль доступа.
Теперь, разобравшись со всем этим, давайте сосредоточимся на сравнениях языков, проведенных в соответствии с Книгой Swift и сравнением с Rust. Во многих местах я буду сокращать, показывая Swift и Rust бок о бок со знаком «длинное тире» (-) между ними. Извините, если форматирование не слишком велико; меня устраивает. ¯ \ _ (ツ) _ / ¯
Основы
Основные типы
- Int - isize
- UInt - использовать
- Int8, Int16, Int32, Int64 - i8, i16, i32, i64
- UInt8, UInt16, UInt32, UInt64 - u8, u16, u32, u64
- Uint32.min, Uint32.max - u32::MIN, u32::MAX
- Double, Float - f64, f32
- Литералы могут быть в шестнадцатеричном формате: 0xFp-2 == 3.75
- Bool - булево
Опции
- Т? - Вариант
- ноль - нет
- значение == nil, значение! = nil - value.is_none(), value.is_some()
- Дополнительная привязка:
- пусть possible_value: Int? = Int ("123") - пусть possible_value = "123" .parse(). Ok()
- если let value = possible_value - если let Some(value) = possible_value
- У вас может быть много необязательных привязок в одном условном выражении:
- если first = Int ("4"), let second = Int ("42"), first <second
- Распаковка значений (! - unwrap()):
- пусть значение: Int = Int ("123")! - let value = "123" .parse(). Ok(). Unwrap() или просто let value = "123" .parse(). Unwrap()
- «Неявно развернутые опции» - это типы, которые разворачиваются автоматически, если используются в необязательном контексте, но оставляются необязательными в противном случае:
- пусть x: Int! = Int ("123") <- добавление! для ввода делает его неявно развернутым необязательным
- пусть y: Int = x <- неявно развернутый
- если let z = x <- оставлено необязательным, проверяется в условном
Константы и переменные
- «Константа» - неизменяемое значение с таким же синтаксисом:
- пусть имя = 10 - пусть имя = 10
- «Переменная» - это изменяемое значение:
- var name = 10 - пусть mut name = 10
- У вас может быть несколько объявлений в одной строке:
- var x = 0, y = 1, z = 2
- Аннотации типа одинаковы, за исключением того, что вы можете определить несколько значений, которые используют аннотацию одного и того же типа:
- var x, y, z: Int
- Имена констант и переменных могут содержать практически любые символы:
- пусть π = 3.14159
- пусть 你好 = "你好 世界"
- пусть 🐶🐮 = "dogcow"
- Не разрешено затенение имен
- печать - это комбинация println! и распечатать! где он ведет себя как println! по умолчанию, но вы можете установить собственный аргумент терминатора в "", чтобы он был похож на print! или что-то другое.
- Интерполяция внутренне поддерживается для строк:
- print ("Ответ \ (add (40, 2))")
- Обратите внимание, что интерполяция может быть любым выражением, включая литералы и вызовы функций.
Комментарии
- Комментарии такие же, включая возможность вложения комментариев в / * * / стиле.
Точка с запятой
- Swift требует наличия точки с запятой в конце оператора только в том случае, если за ним следует другой оператор в той же строке.
Приведение типов
- (Двойное) имя - имя как f64
- Некоторые приведения типов возвращают опции:
- пусть possible_number: Int? = Int ("123")
Псевдонимы типов
- typealias AudioSample = UInt16 - введите AudioSample = u16
Кортежи
- Кортежи такие же, за исключением того, что вы также можете называть части литералов кортежа:
быстрый let status = (код состояния: 200, описание: «ОК») print ("Статус: \ (status.statuscode) (\ (status.description))")
''
Обработка ошибок
Здесь мы видим первое принципиальное отличие Swift от Rust. В Rust ошибки обрабатываются путем представления их как значений варианта типа Result::Error и возврата их вверх по цепочке вызовов через значения Result. Другими словами, успешные возвращаемые значения и возвращаемые значения ошибок по существу следуют одному и тому же пути выхода из вызовов функций. В Swift ошибки представлены значениями, соответствующими протоколу ошибок (трэйту), которые «выбрасываются» в одной точке и «улавливаются» (или нет) где-то в цепочке вызовов. Они идут принципиально отдельным путем от успешных возвращаемых значений.
Функция, которая может вызывать ошибку, должна содержать ключевое слово throws в своем объявлении (перед телом):
func can_throw() throws { vs func does_not_throw() {
При вызове такой функции вы должны префикс вызова с ключевым словом try:
try can_throws()
Чтобы автоматически поймать ошибку на сайте вызова и преобразовать ее в необязательную, используйте try? (эквивалент Result::ok() для возвращаемого значения функции в Rust):
let x: Int? = try? something() — let x: Option<isize> = something().ok()
Чтобы автоматически поймать ошибку на сайте вызова и завершить работу с ошибкой времени выполнения, используйте команду try! (эквивалент Result::unwrap() для возвращаемого значения функции в Rust):
let x: Int = try! something() — let x: isize = something().unwrap()
Вывести ошибки с помощью оператора throw:
throw some_error
Разве не застали на месте звонка с попыткой? или попробуйте !, ошибки «раскручивают стек» или приводят к возврату выполнения кода из функций до тех пор, пока явно не «перехватят». Вы обнаруживаете ошибки за пределами места вызова с помощью оператора «do-catch»:
do {
try something()
} catch some_error {
handle_specified_error(some_error)
} catch {
handle_unspecified_error(error)
}
Обратите внимание, что специальная ошибка имени доступна в неопределенном блоке обработки ошибок.
Утверждения и предварительные условия
- assert и precondition подобны assert! макрос, за исключением того, что assert выполняется только для сборки «Отладка».
- preconditionFailure - это по сути паника !.
Основные операторы
- У Swift есть тернарный оператор a? b: c, в отличие от Rust, где вместо этого вы использовали бы условное выражение if a {b} else {c}.
- Тип String реализует оператор + для конкатенации; в Rust оператор + (или String::push_str) требует, чтобы правая часть была срезом.
- Тип Bool не поддерживает сравнение, в отличие от bool в Rust.
- «Оператор нулевого слияния»: a ?? b - a.unwrap_or_else (|| b)
- Обратите внимание, что b не оценивается, если a не равно nil, поэтому он отображается на Option::unwrap_or_else, а не Option::unwrap_or.
- Операторы диапазона
- Закрытый диапазон: 1 ... 5 - 1 .. = 5
- Полуоткрытый диапазон: 1 .. <5 - 1..5
- Односторонний диапазон: 1 ..., ... 1 - 1 .., ..1
- Нет Swift эквивалента Rust RangeFull (неограниченный диапазон ..)?
Строки и символы
- Строки Swift «состоят из независимых от кодирования символов Unicode» и «построены из скалярных значений Unicode», тогда как в Rust String и str закодированы в UTF-8.
- Swift Character - это один расширенный кластер графемы (последовательность из одного или нескольких скаляров Unicode), тогда как char Rust - это один скаляр Unicode. Например, в Rust «cafe» .chars(). Count() равно 5, а «café» .len() - 6, тогда как в Swift «café» .count равно 4.
- Буква é представляет собой отдельный кластер графемы, состоящий из скаляра Unicode «LATIN SMALL LETTER E» (U + 0065), который представляет собой один байт в кодировке UTF-8, за которым следует скаляр Unicode «COMBINING ACUTE ACCENT» (U + 0301 ), что составляет два байта в кодировке UTF-8.
- В Swift вы можете изменять символы в строках, объединяя скаляры Unicode вместе. Например, "e" + "\ u {301}" == "é".
- Swift String имеет связанный тип String.Index, представляющий позицию символа в строке.
- Строковые методы index (до :), index (после :) и index (_: offsetBy :) используются со свойствами String startIndex и endIndex для создания значений String.Index.
- Свойство index для String в Swift перебирает символы так же, как str::chars в Rust.
- Индексирование или нарезка строки возвращает подстроку, которая похожа на str (содержит ссылку на строку, из которой она была вырезана).
- пусть начало = строка [.. <индекс]
- Преобразование подстроки в строку - это приведение типа
- Вы можете кодировать String в UTF-8 или UTF-16 с помощью свойств utf8 или utf16, которые являются String.UTF8View или String.UTF16View и могут повторяться как последовательность UInt8 или UInt16.
- Вы можете получить скалярные значения Unicode строки с помощью свойства unicodeScalars, которое является UnicodeScalarView и может повторяться как последовательность UnicodeScalar.
- Многострочные строки удаляют пространство с отступом, чего не делает Rust по своей сути, но вы можете получить аналогичное поведение, если используете indoc crate.
- Строки Swift - это типы значений, которые копируются при передаче в функции. Не существует различия типов между изменяемыми и неизменяемыми строками, как в Rust (где String является изменяемым, а str (slice) неизменным).
- Обратите внимание, что в книге упоминается: «Компилятор Swift оптимизирует использование строк, так что фактическое копирование происходит только тогда, когда это абсолютно необходимо», что подразумевает, что реализация, возможно, ближе к Cow <'a, str>?
- «Расширенные разделители строк» похожи на «необработанные строковые литералы» в том, что специальные символы могут быть помещены в строку, не вызывая их эффекта. Однако в Swift вы можете «вызвать эффект вручную», добавив соответствующее количество знаков решетки # после escape-символа \ backslash:
- #"Строка 1\ nСтрока 2" # - r # "Строка 1 \ nСтрока 2" #
- ##"Строка 1 \ nСтрока 2" ## - r ## "Строка 1 \ nСтрока 2" ##
- #"Line 1 \ #nLine 2" # разрывает строку и эквивалентно "Line 1 \ nLine 2" в Swift или Rust; нет способа сделать это с необработанным строковым литералом Rust?
Типы коллекций
В Swift есть три основных типа коллекций:
- Массив
или T - Vec - Установить
- HashSet - Словарь <Ключ, Значение> или Ключ: Значение - HashMap <K, V>
Единственный упорядоченный тип коллекции - это массив.
- Чтобы упорядочить ключи набора, используйте метод sorted(), который возвращает массив ключей, отсортированных по порядку.
- Чтобы упорядочить содержимое словаря, используйте метод sorted() свойств ключей или значений, который возвращает массив ключей или значений, отсортированных по порядку. Ключи Set и Dictionary должны быть хешируемыми, как и в Rust. Протокол Hashable в Swift похож на трейт Hash в Rust.
Использование массивов
[Int32]()
- Vec::::new() - Массив (повторение: 0,0, количество: 3) - vec! [0,0; 3]
- Array поддерживает операторы + и + = для объединения и добавления, тогда как в Rust вам придется начинать с левой стороны, а затем либо добавлять, либо расширять ее.
- Вы можете использовать синтаксис индекса для замены значения по заданному индексу, как в Rust:
values[4]
= "яблоки"
- Вы также можете использовать синтаксис индекса для сращивания:
values4 ... 6]
= ["яблоки", "апельсины"] - values.splice (4 .. = 6, ["яблоки", "апельсины"]. iter(). cloned());
- removeLast() - Vec::pop
Использование наборов
- Установить
() - HashSet:: ::new() - let values: Set
= [1, 2, 3] - let values = hashset! {1, 2, 3} (с использованием контейнера maplit) - count - HashSet::len
- вычитание (_ :) - HashSet::difference
- isStrictSubset (of :) и isStrictSuperset (of :) похожи на isSubset (of :) и isSuperset (of :), за исключением того, что они возвращают false, если наборы равны.
Использование словарей
[Int32: String]()
- HashMap::<i32, String>::new()[:]
- HashMap::new()- let dict = [1: «яблоки», 2: «апельсины», 3: «бананы»] - let dict = hashmap! {1 => «яблоки», 2 => «апельсины», 3 => «бананы» } (используя картонный крэйт)
- count - HashMap::len
- dict.updateValue ("answer", forKey: 42) (или dict [42] = "answer", если дополнительное старое значение не требуется) - dict.insert (42, "answer")
- dict [42] - dict.get (42)
- dict [42]! - dict [42]
Поток управления
- Поток управления использует семантику операторов, поэтому их нельзя использовать в контекстах выражений.
- Например, это действует в Rust, но не в Swift: let x = if y {1} else {2} (вместо этого можно использовать тернарный оператор y? 1: 2)
- Еще одним следствием этого является то, что операторы break не могут принимать аргумент.
- Swift имеет дополнительный синтаксис «repeat-while», аналогичный синтаксису «do-while» в C, но ему не хватает синтаксиса loop infinite loop и while let.
- Имена меток для циклов (где продолжение или разрыв вложенного цикла может перейти в помеченный внешний цикл) должны начинаться с символа кавычки в Rust, но не в Swift.
- Переключатель Swift похож на матч в Rust, за исключением:
- Ключевое слово case присутствует перед каждой рукой, как и C.
- Ключевое слово по умолчанию без регистра заменяет _, чтобы соответствовать любым случаям, не охваченным иным образом.
- В теле каждого дела должно быть хотя бы одно заявление.
- Альтернативные шаблоны разделяются запятой, а не вертикальной чертой |.
- Привязка значений в шаблонах требует размещения ключевого слова let перед именем для привязки:
- case (пусть x, 0): - (x, 0) =>
- Swift использует ключевое слово where вместо if для «дополнительных условий» (называемых в Rust «защитой от совпадений»).
- case let (x, y), где x == y: - (x, y) if x == y =>
- В Swift есть дополнительный оператор перехода управления, который вы можете использовать в качестве последнего оператора кейса в переключателе, чтобы имитировать поведение переключателя в C, когда отсутствие разрыва приводит к тому, что выполнение выполняется с конца. одного дела к началу следующего дела.
- Swift имеет дополнительный синтаксис «охранника», предназначенный для использования в сценариях «раннего выхода». Он выглядит аналогично оператору if, за исключением того, что if заменяется на guard, ключевое слово else помещается перед телом, а любое значение, связанное с условием (с помощью guard let), ограничивается блоком, содержащим охранник. Операторы тела выполняются только в том случае, если условие ложно, и обычно содержат возврат «раннего выхода».
- Условная компиляция в Swift выполняется с использованием специального условного оператора, который вы можете использовать в операторах if и guard, в отличие от атрибута cfg в Rust.
- Единственное специальное условие, упомянутое в книге для использования при условной компиляции, - это условие #available, используемое для условной компиляции на основе целевой платформы.
Функции
- func - fn
- Если все тело функции является одним выражением, функция неявно возвращает это выражение. Rust поддерживает это, но также позволяет использовать операторы перед выражением, которые Swift не поддерживает.
- Аргументы «помечены», и такие метки аргументов считаются частью имени функции. Это дает им привкус "Objective-C", предназначенный для совместимости с этим языком.
- Например: func Speak (toPerson person: String, what speech: String)
- Название этой функции в целом считается говорящим (toPerson: what :).
- Если не указано иное, метка совпадает с названием:
- func Speak (person: String, what speech: String) совпадает с func speak (person: String, what speech: String)
- Метку можно опустить, заменив ее знаком подчеркивания (_) в объявлении функции.
- func speakTo (_ person: Строка, какая речь: Строка)
- В этом случае считается, что имя функции - speakTo (_: what :)
- func speakTo (_ person: Строка, какая речь: Строка)
- При вызове функции необходимо использовать не пропущенные метки.
- Например: speakTo («Фрэнк», что: «Привет!»)
- Хотя имена должны быть уникальными, метки не обязательно должны быть уникальными (но обычно для удобства чтения).
- Например: func Speak (toPerson person: String, what speech: String)
- Аргументы могут иметь значения по умолчанию, указанные в объявлении функции путем добавления после типа знака равенства (=), за которым следует значение. Если аргумент имеет значение по умолчанию, его можно не указывать при вызове функции.
- Функции поддерживают переменные параметры. Если за типом параметра следует ..., фактическим типом параметра будет массив указанного типа, в то время как вызывающие объекты предоставляют значения для массива без использования синтаксиса массива.
- Обратите внимание, что функция может иметь не более одного вариативного параметра, но не обязательно, чтобы он был последним параметром?
func average(_ numbers: Double...) -> Double {
/* body omitted for brevity */
}
average(1, 2, 3)
average(1.2, 3.5)
- Параметры по умолчанию являются постоянными (неизменяемыми).
- Параметры передаются по значению (копируются, как в трейте Copy в Rust), если они являются фундаментальными типами, строками или структурами. В противном случае они передаются по ссылке.
- Размещение ключевого слова inout перед типом параметра в объявлении функции дает параметру семантику «копирование-вход-копирование-выход» (которое может быть оптимизировано до «передачи по изменяемой ссылке», но вы не должны рассчитывать на это).
- При вызове функции параметр копируется.
- Во время вызова функции параметр является изменяемым и должен рассматриваться отдельно от исходного значения (хотя его можно оптимизировать, чтобы он был таким же адресом в памяти).
- Когда функция возвращается, параметр копируется обратно вызывающей стороне, заменяя исходное значение.
- С целью определения типа функции без возвращаемого значения Void заменяет() в качестве возвращаемого типа.
- Функции, передаваемые в качестве параметров, объявляются без каких-либо ключевых слов, например func:
- func useFunction (_ function: (Int) -> Int) - fn use_function (функция: Fn (isize) -> isize)
- В отличие от Rust, в Swift нет различий между типами замыкания / функции «по значению» (FnOnce), «изменяемым» (FnMut), «неизменяемым» (Fn) или «указателем на функцию» (fn).
- Функция, объявленная внутри другой функции, технически является закрытием в Swift (имеет доступ ко всем значениям в области внешней функции), тогда как в Rust такая функция не является закрытием (хотя вы можете сделать внутреннюю функцию закрытием в Rust. сделать то же самое).
Замыкания
- {(x: Int, y: Int) -> Int в x * y} - | x: isize, y: isize | х * у
- {x, y в x * y} - | x, y | х * у
- Имена аргументов могут быть полностью опущены, если типы могут подразумеваться; в этом случае специальные имена, образованные символом $, за которым следует индекс аргумента, представляют каждый аргумент
- {$ 0 * $ 1} - | x, y | х * у
- Операторные методы, вероятно, являются самым коротким синтаксисом для закрытия. Например, names.sorted (by:>) сортирует имена с помощью оператора> типа элементов в именах.
- Swift имеет синтаксис «завершающего закрытия», в котором вы можете переместить параметр закрытия в блок сразу после круглых скобок при вызове функции. Например, names.sorted (по: {$ 0> $ 1}) можно вместо этого записать как names.sorted() {$ 0> $ 1}.
- Метка аргумента для первого замыкающего конца опущена. Если есть дополнительные замыкающие замыкания, они следуют за первым замыкающим телом замыкания и продолжаются их метками аргументов.
- Если нет аргументов, кроме замыкающего (ых) замыкания (ов), скобки в самом вызове функции также могут быть опущены. Например: names.sorted {$ 0> $ 1}.
- Замыкания захватывают константы и переменные из окружающего контекста по ссылке. В качестве оптимизации компилятор может вместо этого захватить их по значению, если они не изменены замыканием или каким-либо кодом, выполняемым после создания замыкания.
- Замыкания являются ссылочными типами (может быть очевидным, но в книге есть специальный раздел, чтобы подчеркнуть этот момент).
- Замыкания могут иметь «список захвата», в котором вы явно контролируете, какие значения из окружающего контекста захватываются и как они захватываются.
- Если присутствует, список захвата находится первым внутри открывающей фигурной скобки ({) закрытия.
- В квадратных скобках поместите список элементов, разделенных запятыми, где каждый элемент является значением для захвата, необязательно с ключевым словом weak или unowned впереди, чтобы указать, должна ли ссылка быть слабой или не учитываться вообще (соответственно). Каждое значение также может быть связано с именем, локальным для закрытия. Например:
[self]
- зафиксировать ценностное «я» с помощью сильной ссылки.- [слабое «я»] - зафиксировать ценностное «я» по слабой ссылке
- [weak delegate = self.delegate] - захватить значение self.delegate по слабой ссылке с именем delegate.
- Замыкание, которое «экранирует» функцию (вызывается после возврата из функции), должно быть помечено @escaping перед типом замыкания в списке параметров функции.
- Пометка параметра замыкания с помощью @autoclosure перед типом замыкания в списке параметров функции позволяет вызвать функцию и передать выражение, которое автоматически переносится замыканием. Другими словами, выражение, переданное функции, "лениво вычисляется" или не вычисляется до тех пор, пока функция не вызовет закрытие.
Перечисления
- При перечислении вариантов перечислений каждая строка должна начинаться с ключевого слова case.
- В каждой строке может быть объявлено несколько вариантов, если они разделены запятыми.
- Синтаксис для указания варианта: TypeName.variant или просто .variant, если можно вывести TypeName.
- Добавляя: CaseIterable после имени перечисления в его объявлении (другими словами, указывая, что перечисление соответствует протоколу CaseIterable), Swift добавит свойство allCases, которое представляет собой коллекцию всех вариантов.
- Значения перечисления могут быть напечатаны без реализации какого-либо протокола.
- При включении перечисления связанные значения (если они есть) привязываются к именам путем размещения let с последующим именем в шаблоне case или путем размещения одного let сразу после ключевого слова case.
- В качестве альтернативы связанным типам перечисления могут обрабатывать «необработанные значения» путем добавления двоеточия, за которым следует тип исходного значения после имени перечисления. Каждому варианту присваивается соответствующее необработанное значение, которое может быть присвоено по умолчанию или назначено путем предоставления буквального значения после имени варианта со знаком равенства (=) между именем и значением. Типы необработанных значений могут быть символами, строками, целыми числами или числами с плавающей запятой. Вариантам, которым не присвоено явное исходное значение, присваивается одно значение в соответствии с их положением и типом:
- Целые числа по умолчанию равны 0 для первого варианта или значение предыдущего варианта плюс один.
- По умолчанию строки соответствуют имени самого варианта.
- Перечисления с исходными значениями автоматически получают метод инициализатора, который принимает исходное значение надлежащего типа и возвращает необязательное значение перечисления (вариант, который соответствует заданному исходному значению, или ноль, если нет соответствующего варианта для исходного значения) .
- Вариант перечисления может иметь связанное значение, которое имеет тот же тип перечисления, если вариант помечен ключевым словом косвенное перед регистром варианта. Это заставляет Swift добавлять косвенное обращение к варианту, так же, как вам пришлось бы сделать это вручную, используя Box
в Rust, чтобы добиться того же.
Структуры и классы
- И структуры, и классы в Swift похожи на структуры в Rust.
- У классов есть дополнительные возможности, которых нет у структур:
- Наследование (вообще не поддерживается напрямую в Rust)
- Типовое приведение
- Деинициализаторы (аналог трейта Drop в Rust)
- Подсчет ссылок (аналогично типам Rc и Arc в Rust)
- Структуры (и перечисления) являются «типами значений», то есть они копируются при назначении или передаче в функции.
- Классы являются «ссылочными типами», что означает, что они ведут себя так, как если бы они были неявно заключены в Rc / Arc, где ссылка добавляется при назначении или передаче функциям, а значение освобождается только тогда, когда счетчик ссылок становится равным нулю.
- Используйте ключевое слово class вместо struct, чтобы создать класс, похожий на C++.
- Поля определены (и также могут быть инициализированы) в определении типа с использованием того же синтаксиса, что и объявление и присвоение переменных, включая возможность позволить компилятору определять типы (в отличие от Rust, где тип каждого поля должен быть явно определен, и инициализация происходит в методе или явном создании экземпляра структуры).
- Чтобы отличить равенство от идентичности, Swift определяет оператор === (и обратный! ==) для ссылок, где === аналогичен Arc::ptr_eq, возвращая истину, если операнды относятся к одному и тому же значению.
- Ссылки Swift автоматически разыменовываются при использовании; нет синтаксиса для «разыменования» или для создания новой ссылки (кроме присвоения ссылки или передачи ее функции). Другими словами, ссылки - это не типы первого класса в Swift?
Характеристики
- Свойства либо «хранятся» (как в Rust), либо «вычисляются».
- Сохраненное свойство, помеченное ключевым словом lazy перед его объявлением, не вычисляется до первого использования.
- «Вычисляемое» свойство может быть определено для классов, структур и типов перечислений. Они объявляются аналогично свойствам с аксессорами в классах C#:
- После имени типа свойства идет блок, содержащий два внутренних блока, один с get перед ним, а другой с set перед ним. Эти внутренние блоки похожи на функции, вызываемые, соответственно, при чтении или записи свойства.
- Ожидается, что блок get вычислит значение свойства и вернет его с помощью оператора return.
- Поддерживается сокращенный синтаксис, когда внутренний блок получения является одним выражением. В этом случае это выражение вычисляется для вычисления значения свойства без необходимости в явном операторе возврата.
- Блок set получает аргумент значения, который является выражением, присвоенным свойству. Это значение можно определить двумя способами:
- Помещая имя в круглые скобки между набором и его блоком кода, значение получает это имя.
- В противном случае в блоке кода определяется неявное значение с именем newValue.
- Вычисляемое свойство, доступное только для чтения, объявляется путем отбрасывания ключевого слова get, сохранения блока операторов, которые должны были появиться после get, и полного удаления ключевого слова set и его блока кода.
- Swift поддерживает «наблюдателей за свойствами», которые похожи на функции, вызываемые автоматически при установке значения свойства. Они могут быть добавлены для сохраненных свойств, а также унаследованных вычисляемых свойств (для ненаследственных вычисляемых свойств вы помещаете код наблюдателя непосредственно в «установщик» свойства).
- Наблюдатели свойств добавляются в синтаксисе, аналогичном вычисляемым свойствам, с блоком после типа и значения по умолчанию (если есть) свойства, а внутренние блоки обрабатываются ключевыми словами willSet и didSet.
- И willSet, и didSet, и их блоки кода не являются обязательными. У вас может быть один или оба.
- Блок willSet имеет тот же синтаксис, что и блок set (за исключением того, что ключевое слово отличается) и вызывается непосредственно перед установкой свойства.
- Блок didSet имеет тот же синтаксис, что и блок set, за исключением ключевого слова и имени по умолчанию для предоставленного ему аргумента oldValue, а не newValue. Блок didSet вызывается сразу после установки свойства. Предоставляемый аргумент - это значение, которое свойство имело до того, как оно было установлено.
- Наблюдатели свойств добавляются в синтаксисе, аналогичном вычисляемым свойствам, с блоком после типа и значения по умолчанию (если есть) свойства, а внутренние блоки обрабатываются ключевыми словами willSet и didSet.
- Swift поддерживает «оболочки свойств», которые представляют собой специальные структуры, которые представляют свойства и предоставляют повторно используемый код для методов получения и установки свойств.
- Они объявлены как обычные структуры, но с @propertyWrapper впереди.
- Они должны предоставлять вычисляемое свойство с именем wrappedValue, которое предоставляет средства получения и установки для обернутого свойства.
- Оболочки свойств применяются к вычисляемому свойству путем добавления перед вычисляемым свойством имени оболочки свойства с @ (другими словами, имя оболочки свойства, преобразованное в атрибут).
- Инициализаторы, предоставляемые с оболочками свойств, могут использоваться для установки начального значения обернутых свойств.
- Обернутое свойство без предоставленного значения по умолчанию использует инициализатор init() для инициализации свойства.
- Обернутое свойство с одним присвоенным ему значением использует инициализатор init (wrappedValue :) для инициализации свойства.
- Можно использовать другие инициализаторы, расширив атрибут оболочки свойства, применяемый к вычисляемому свойству, чтобы он выглядел как вызов функции, с предоставленными метками и значениями, которые должны соответствовать одному из инициализаторов инициализации оболочки свойства.
- Они поддерживают «прогнозируемые значения», которые отображаются так, как если бы тип, содержащий вычисляемое свойство, имел второе вычисляемое свойство только для чтения с тем же именем, но с префиксом знака доллара ($). Чтобы добавить прогнозируемое значение, оболочка свойства объявляет собственное свойство с именем projectedValue, которое предоставляет прогнозируемое значение.
- Вычисляемые свойства и наблюдатели свойств также могут применяться к глобальным и локальным переменным, которые действуют как сохраненные свойства структур.
- Глобальные переменные действуют так, как если бы они были объявлены с помощью ключевого слова lazy, в том смысле, что они не инициализируются до первого использования.
- Swift поддерживает «свойства типа», которые являются свойствами, применяемыми к самим типам (а не значениям типа). К ним перед объявлением свойства добавлено ключевое слово static.
- Свойства типа доступны через само имя типа, как если бы тип был структурой.
Методы
- Классы, структуры и перечисления могут иметь методы.
- Методы экземпляра похожи на функции, объявленные в блоке impl для типа Rust, за исключением того, что в Swift они помещаются внутри самого объявления типа, аналогично тому, как методы объявляются в C++.
- Методы имеют доступ к свойствам своего экземпляра, как если бы имена свойств были в пределах области действия метода (аналогично C++).
- Специальное неявное свойство self доступно для методов, привязанных к самому экземпляру. Это похоже на значение this в методах C++ и аналогично self в функциях Rust impl, за исключением того, что оно неявно (как если бы оно было объявлено как &mut self для классов или &self для структур и перечислений).
- Чтобы сделать структуры и перечисления самостоятельно изменяемыми, добавьте ключевое слово mutating перед ключевым словом func в объявлении метода.
- Обратите внимание, что вы не можете вызвать «изменяющийся» метод для константной структуры или перечисления.
- Вы можете присвоить себе "изменяющийся" метод, чтобы заменить значение новым. Это может быть полезно, например, в перечислениях для изменения варианта.
- Методы типа похожи на методы экземпляра, за исключением того, что методы вызываются для самого типа, self относится к типу, а не к какому-либо конкретному значению типа, а любые неквалифицированные имена методов или свойств, используемые в методе, относятся к другим методам уровня типа. и свойства типа, а не какого-либо конкретного значения типа.
- Чтобы объявить метод типа, добавьте ключевое слово static перед объявлением метода.
- Методы класса подобны методам типов, за исключением того, что они могут быть переопределены в подклассах (и, следовательно, могут быть объявлены только в классах).
- Чтобы объявить метод класса, добавьте ключевое слово class перед объявлением метода.
- В отличие от Rust, в Swift вы можете иметь «перегруженные» методы или несколько методов с одним и тем же именем, но разных типов для параметров и/или возвращаемого значения.
Индексы
- «Индексы» Swift похожи на трэйты Index и IndexMut в Rust, что позволяет типам определять специальные методы, которые будут использоваться, если экземпляры индексируются.
- Синтаксис индексов представляет собой сочетание синтаксиса функции и вычисляемого свойства.
- Их объявления начинаются как функции, за исключением того, что нет ключевого слова func, а имя заменяется ключевым словом subscript.
- У них есть блоки get и set в теле, аналогичные вычисляемым свойствам.
- Блок get соответствует Index::index в Rust.
- Блок set соответствует IndexMut::index_mut в Rust, за исключением того, что вместо возврата ссылки он принимает значение, которое нужно присвоить, как установщик вычисленных свойств.
- Индексы могут иметь несколько параметров, которые разделяются запятыми как при объявлении, так и при использовании индексов.
- «Индекс типа» или «индекс класса» аналогичен «методу типа» или «методу класса» соответственно. Они объявляются как обычный нижний индекс, но с ключевым словом static или class перед объявлением. Они позволяют индексировать сам тип в целом.
Наследование
Поскольку наследование полностью чуждо для Rust, в этом разделе будет сравниваться наследование в Swift с наследованием в C++.
- Свойства могут быть переопределены для предоставления настраиваемых методов получения, установки или наблюдателей для любого унаследованного свойства. К переопределенному свойству добавлено ключевое слово override аналогично переопределенному методу.
- Свойство только для чтения может быть изменено на чтение и запись.
- Свойство чтения-записи не может быть переопределено как доступное только для чтения.
- Можно предотвратить переопределение свойств и методов, добавив ключевое слово final перед их объявлениями в суперклассе.
- Можно предотвратить создание подкласса всего класса, добавив ключевое слово final перед его объявлением.
- В Swift нет «чисто виртуальных» классов или методов. Вместо этого используйте «протоколы».
- «Множественное наследование» (подкласс, имеющий два или более суперкласса) не поддерживается.
Инициализация
- «Инициализатор» похож на конструктор C++; объявлен как метод с именем init, но без ключевого слова func и вызывается, когда тип используется как функция, чтобы обеспечить инициализацию всех свойств нового экземпляра. Это примерно аналогично новому соглашению о функциях и трейту Default в Rust, за исключением того, что экземпляр предоставляется через неявное значение self, а не возвращается.
- Инициализаторы по умолчанию создаются для каждого типа структуры / класса (при условии, что все поля имеют значения по умолчанию и тип не предоставляет хотя бы один явный инициализатор).
- Поэлементные инициализаторы также создаются для каждого типа структуры / класса (если они не предоставляют хотя бы один явный инициализатор). Они такие же, как инициализаторы по умолчанию, за исключением того, что они принимают параметры с метками, соответствующими именам полей типа.
- Все свойства значения должны быть инициализированы к моменту возврата инициализатора. Свойства могут быть инициализированы либо в самом объявлении свойства, либо в коде инициализатора.
- Инициализаторы могут вызывать другие инициализаторы того же типа, чтобы уменьшить дублирование кода.
- Классы могут иметь специальные инициализаторы, называемые «инициализаторами удобства» (объявляются путем добавления ключевого слова удобства перед инициализатором). Они не используются неявно подклассами, но могут использоваться для прямой инициализации значений, а также другими инициализаторами.
- Инициализаторы суперкласса вызываются из инициализаторов подкласса через имя super, например: super.init().
- Инициализаторы, которые не являются «удобными», называются «назначенными инициализаторами».
- Swift применяет правила о том, как инициализаторы могут вызывать другие инициализаторы в иерархии наследования класса:
- Назначенные инициализаторы должны вызывать назначенный инициализатор из своего непосредственного суперкласса, если таковой имеется.
- Инициализаторы для удобства должны вызывать другой инициализатор из того же класса.
- Удобные инициализаторы должны «в конечном итоге» (напрямую или через другой удобный инициализатор) вызывать спроектированный инициализатор.
- Swift применяет правила инициализации свойств в иерархии наследования класса:
- Назначенный инициализатор должен гарантировать, что все ненаследуемые свойства инициализированы перед вызовом назначенного инициализатора суперкласса.
- Назначенный инициализатор не может назначать унаследованное свойство до тех пор, пока не будет вызван назначенный инициализатор суперкласса.
- Удобный инициализатор должен вызывать назначенный инициализатор перед назначением какому-либо свойству.
- Инициализатор не может вызывать какие-либо методы типа, читать любые значения свойств или ссылаться на self как на значение до тех пор, пока не будут возвращены все назначенные инициализаторы суперкласса.
- Инициализатор можно пометить как ошибочный с помощью init? вместо init в качестве имени инициализатора.
- Такой инициализатор может использовать оператор return nil, чтобы указать, что инициализация не удалась.
- Вызов такого инициализатора дает необязательное значение.
- Используя init! вместо init? инициализатор создает экземпляр, который «неявно разворачивается».
- Суперкласс может потребовать от всех подклассов предоставить инициализатор определенной формы (параметров), объявив свой собственный инициализатор с этой формой и добавив необходимое ключевое слово впереди.
- Значения свойств по умолчанию могут быть указаны как закрытие или вызовы функций, чтобы эти значения по умолчанию были вычислены во время инициализации.
Деинициализация
- Swift поддерживает «деинициализаторы» для классов (не структур или перечислений). Они аналогичны трейту Drop в Rust.
- Деинициализатор объявлен как инициализатор без параметров, но с именем deinit вместо init.
Необязательная цепочка
Необязательная цепочка в Swift относится к использованию оператора вопросительного знака (?) В выражении, которое вызывает свойство или метод необязательного значения. Это аналог Option::map или Option::and_then в Rust.
Например, в Swift:
let result: SomeType? = value.property?.change()
Это аналогично следующему в Rust:
let result: Option<SomeType> = value.property().map(Property::change)
Можно думать об этом так, что цепочка вызовов состоит из промежуточных необязательных значений и останавливается, если в любой точке промежуточное значение равно нулю (аналогично None в Rust).
Необязательная цепочка, которая заканчивается присваиванием, приводит к тому, что это присваивание происходит только в том случае, если все левое выражение не равно нулю.
Swift автоматически "сглаживает" оцененные значения в необязательной цепочке, тогда как в Rust разработчик должен выбрать либо Option::map, либо Option::and_then в зависимости от того, возвращается ли необязательное значение или необязательное значение в любой заданной точке для избегать вариантов "вложенности". Другими словами, в Swift:
- Если необязательное значение оценивается в необязательной цепочке, оно станет необязательным.
- Если необязательное значение оценивается в необязательной цепочке, оно остается необязательным (например, не становится чем-то вроде Option<Option
>).
Обработка ошибок
Обработка ошибок в основном уже рассматривалась в Основах. По сути, Swift использует подход «исключение / перехват» для обработки ошибок, тогда как Rust вместо этого использует подход «возврат успеха / неудачи».
Swift также поддерживает специальный блок кода, называемый «оператором отсрочки». Добавляя ключевое слово defer перед вложенным блоком кода, компилятор выполнит код внутри блока непосредственно перед выходом из включающей области. Это полезно для кода «очистки», который необходимо запустить в случае возникновения ошибки, вызывающей раскручивание текущей области. Операторы отложенного выполнения выполняются в порядке, обратном порядку их написания.
Приведение типов
При работе с классами в системе типов Swift необходимы два дополнительных оператора из-за характера наследования классов:
- Оператор is используется для определения во время выполнения, принадлежит ли значение определенному типу подкласса. Это похоже на Any::в Rust.
- Например: let isApple: Bool = (фрукт - это яблоко)
- Чем как? и в качестве! операторы используются для «понижения» значения до типа подкласса. То как? оператор возвращает необязательное значение подкласса. Как! оператор возвращает значение подкласса или вызывает ошибку времени выполнения, если значение не относится к типу подкласса. Они похожи на Any::downcast_ref в Rust.
Вложенные типы
Swift, в отличие от Rust, поддерживает «вложенные типы», то есть типы, объявленные в рамках другого типа. Они представляют собой просто синтаксический сахар для целей группировки типов или ограничения типов, используемых в реализациях, от отображения в общедоступном интерфейсе.
Расширения
Swift поддерживает добавление вычисляемых свойств, методов, инициализаторов, индексов, вложенных типов и реализаций протокола типов к существующим типам с помощью специального синтаксиса. К имени существующего типа добавляется префикс ключевого слова extension, за которым следует блок, содержащий новые свойства, методы и т.д. Внутри. Все это синтаксический сахар, создающий видимость расширения типа, когда на самом деле новая функциональность добавляется вместе с существующим типом. Это похоже на «свойства расширения» в Rust?
Протоколы
Протоколы в Swift аналогичны «трэйтам» в Rust.
- Протоколы объявляются с использованием ключевого слова protocol, за которым следует имя протокола, за которым следует блок, содержащий объявления требований любого типа, реализующего протокол.
- Требования к свойствам указываются как вычисляемые свойства без блоков операторов.
- Требования к методам указаны так же, как и к методам, но без тел.
- Как и в случае с суперклассами, протоколы включают необходимое ключевое слово с требованиями инициализатора.
- Типы, реализующие протоколы, перечисляют протоколы после имени типа. Если реализовано более одного протокола, все они перечислены вместе через запятую.
- Протоколы Swift могут использоваться как типы для значений. Такие значения аналогичны «объектам трэйтов» в Rust, размещаются в куче и всегда обрабатываются указателем с подсчетом ссылок, хотя Swift скрывает большую часть этих деталей от программиста.
- Универсальные типы в Swift могут условно соответствовать протоколу, перечисляя ограничения на тип, аналогично границам трэйтов в Rust.
- Swift предоставляет автоматические («синтезированные») реализации протоколов Equatable, Hashable и Comparable для типов, которые придерживаются определенных правил, специфичных для этих протоколов.
- Equatable - PartialEq
- Hashable - Хеш
- Сопоставимый - PartialOrd
- Протоколы могут быть ограничены для использования классами только путем наследования от специального протокола AnyObject.
- Состав протокола - это способ объединения нескольких требований протокола для значения определенного типа. Он указывается аналогично тому, как несколько границ черт указываются в Rust, за исключением того, что & используется для разделения нескольких протоколов k, а не +, используемого для разделения нескольких черт в границах черт Rust.
- То есть, как?, И как! операторы работают с прокотолами так же, как и с подклассами.
- Для взаимодействия с Objective-C Swift позволяет протоколам, помеченным атрибутом @objc, содержать «необязательные требования» или спецификации свойств или методов, которые могут или не могут быть реализованы для типа.
- Такие требования содержат ключевые слова @objc и опционально впереди.
- Метод «необязательно» - это, по сути, «необязательная функция». ? Оператор может использоваться в вызове такого метода, чтобы использовать «необязательную цепочку» для проверки во время выполнения, чтобы увидеть, доступен ли метод для значения перед его вызовом: someOptionalMethod? (someArgument)
- Реализации по умолчанию для требований протокола могут быть указаны с помощью расширений свойств. Это делает такие требования по существу «необязательными» для реализуемых типов; если тип реализует это, используется эта реализация, в противном случае используется реализация по умолчанию.
Дженерики
- Объявление расширения для универсального типа не повторяет объявление параметров типа.
- Общие связанные типы объявляются в протоколах с использованием ключевого слова connectedtype (в Rust вы бы просто использовали ключевое слово type). При реализации такого протокола выводится связанный тип, в отличие от Rust, где вы назначили его заполнителю связанного типа при реализации универсального.
- struct Stack
: Container - type Item = Element
- struct Stack
- Ограничения связанного типа объявляются вместе со связанным типом в протоколе, тогда как в Rust вы должны применять ограничения в блоках или методах реализации, где эти ограничения необходимы.
- Swift поддерживает спецификации ограничений типа для параметров универсального типа с использованием предложений where, аналогичных Rust; однако в предложениях может быть указано, что типы должны быть равными, в дополнение к тому, что типы реализуют определенные протоколы. Требование равенства типов в Rust эквивалентно использованию «привязок связанных типов». Например:
- Swift: func foo <I: Iterator> (it: I), где I.Item == Foo
- Rust: fn foo (it: I), где I: Iterator <Item = Foo>
- Расширения универсальных шаблонов могут также добавлять собственные спецификации ограничений типа.
Непрозрачные типы
Ключевое слово some в Swift используется аналогично ключевому слову impl, особенно в позиции возвращаемого значения, для обозначения «некоторого типа, который реализует протокол (трэйт)», где компилятор может определить фактический конкретный тип:
- -> some Shape — -> impl Shape
Автоматический подсчет ссылок
- Слабые ссылки: weak var tenant: Person? - пусть мут-арендатор: Слабый <Личность>
- «Необязательная ссылка» имеет ключевое слово unowned перед объявлением необязательного значения. Такая ссылка не участвует в автоматическом подсчете ссылок, но, в отличие от слабого указателя, она не является «необязательной» (не может быть нулевой). «Висячая» ссылка, не имеющая владельца, при обращении к ней вызывает ошибку времени выполнения. В Rust нет четкого аналога этому; * const T и * mut T близки к реализации этой концепции, но не совпадают точно?
- «Необязательная ссылка без владельца» - это то же самое, что и «Необязательная ссылка», за исключением того, что значение является необязательным типом. Это делает его аналогом * const T или * mut T в Rust?
Безопасность памяти
По сути, Swift выполняет ограниченную форму «проверки заимствования», обнаруживая «конфликты памяти», когда есть некоторое перекрытие (множественное заимствование) при обращении к значениям. В книге приведены следующие примеры:
- Параметры ввода-вывода: передача значения параметра ввода-вывода функции, которая имеет доступ к тому же значению.
- «Self»: передача значения параметра inout методу с таким же значением.
- Передача двух частей одного и того же кортежа в качестве параметров inout в функцию
Контроль доступа
- «Модуль» в Swift - это «крэйт» в Rust.
- Уровни доступа относятся к модулю и исходному файлу.
- Swift имеет более тонкий контроль доступа, чем Rust:
- open - как общедоступный, но позволяет создавать подклассы в других модулях.
- public - по сути паб в Русте
- internal - по сути паб (крэйт) на Rust
- fileprivate - аналог pub (self) в Rust, если находится в модуле верхнего уровня для исходного файла.
- private - аналог приватного в C++; не применимо к Rust.
Расширенные операторы
- &+ — {integer}::overflowing_add
- &- — {integer}::overflowing_sub
- &* — {integer}::overflowing_mul
- static func + — std::ops::Add
- static prefix func - — std::ops::Neg
- static postfix func ! для постфиксных операторов (нет эквивалента в Rust?).
- В Swift можно определять собственные операторы, например: static prefix func +++.
- Для настраиваемых инфиксных операторов вы можете определить, к какой группе приоритета операторов они должны принадлежать следующим образом: infix operator + -: AdditionPreference.
Заключительные мысли
Если вы попали сюда, прочитав весь пост, Поздравляем! Если вы прыгнули и просто прочитали интересующие вас разделы, это тоже нормально. Спасибо за чтение. Пожалуйста, дайте мне знать в комментариях, если есть какие-либо ошибки, вещи, которые я пропустил, и способы, которые я могу улучшить.