От переводчика: решил взяться за перевод цикла, в котором автор параллельно изучает Rust и Swift и сравнивает их между собой. Старался сохранить стиль и манеру письма оригинала, хотя местами и позволял себе сократить текст (надеюсь) без потери смысла.
Автор вначале кажется слишком предвзятым в сторону Rust, но потом его суждения становятся более взвешенными. Правда, сам я со Swift очень поверхностно знаком, а в Rust, хотелось бы думать, кое-что понимаю, так что тоже не являюсь беспристрастным в этом вопросе. Сравнение становится более интересным, начиная с четвёртой части, но, как говорится, из песни слова не выкинешь.
Rust и Swift
Сравнивая два увлекательных, новых и, очевидно, (хотя и не всегда) похожих языка программирования.
Предыстория
Летом 2015 года я начал изучать Rust. Затем, в сентябре 2015, я взялся за Swift. На первый взгляд, сходство между двумя языками очевидно, и они достигли стабильной версии примерно в одно время: релиз Rust 1.0 состоялся в мае 2015, а релиз Swift 2.0 (который фактически похож на 1.0, поскольку 1.0 служил публичной бетой) — в июне 2015. Оба вдохновлялись такими языками, как Haskell, в то же время сохраняя С-подобный (на самом деле, конечно, ALGOL-подобный) синтаксис, более привычный многим разработчикам, на которых ориентированы эти языки.
Так что, когда я начал книгу про Swift, я не мог удержаться от сравнения. Хотя оба языка кажутся очень похожими, они также очень сильно отличаются в терминах языкового дизайна и философии — и эти отличия очень интересны!
Несколько примечаний
Я ни в коем случае не гуру в теории языков программирования. (Или, по крайней мере, не был. Этот проект, вместе с несколькими другими, может сделать из меня такого). Также я не эксперт. Мои мнения обдуманные, но не обязательно всеохватывающие, и вы найдёте места, в которых я добавил исправления, по мере того как узнавал новую информацию из отзывов.
Я открыто признаю, что склоняюсь в сторону Rust. Мне нравится Swift, но я действительно полюбил Rust. Думаю будет честным признать, что это дело моих личных языковых предпочтений. Я люблю Python больше, чем Ruby, и по ощущениям Rust больше похож на Python, а Swift на Ruby.
Теперь полностью открываю карты — я тоже внёс свою лепту в будущее Rust — я веду о нём подкаст, который приносит мне (совсем немного) денег.
Ещё одно: в феврале 2016 у меня не было возможности написать ничего серьёзного ни на одном из этих языков, хотя я надеюсь изменить ситуацию в течение этого года. Таким образом у меня сложилось много поверхностных впечатлений об этих языках. Мои мысли и чувства о них могут измениться со временем.
И последнее: эта серия статей в прямом смысле слова началась с комментариев в Slack. В следующих частях вы заметите существенные изменения тона, структуры и содержимого. Первая часть была всего лишь моими первыми впечатлениями от прочитанного. Начиная со второй части, я подошёл к этому более осмысленно. В следующих частях этот подход усиливался, что в том числе было обусловлено моей работой над подкастом «New Rustacean».
Rust и Swift (I)
Мысли после чтения вступления к книге о Swift
— ..< – серьёзно?
Это, должно быть, один из наиболее раздражающих операторов, которые я когда-либо видел. Он усложняет восприятие, потому что "<имя", на первый взгляд, воспринимается как начало дженерика и вам приходится заново пересматривать и осмысливать.
После первой главы руководства по Swift моё впечатление было следующим – «жалкое подобие Rust». Это основывалось на первом подходе и всём, что я видел и читал про Swift за последние два года: грубо говоря, это синтаксис Rust и стремление к безопасности, заменённое на совместимость с семантикой Objective-C. (Отдадим должное Apple — эта совместимость, вероятно, была необходима).
Пример, явно иллюстрирующий разницу в подходе, это то, как передаются структуры: по ссылке или по значению (передача копии). В Swift для этого используются две отдельных языковых конструкции: структуры и классы соответственно.
В Rust существует только один тип структур для обоих случаев. Они иммутабельны, если не объявлены как mut, и вы можете передать их по значению, реализовав Copy трейт (что выглядит примерно похожим на protocol в Swift, но я ещё не копнул настолько глубоко, чтобы увидеть разницу). То есть, вместо внесения этих сущностей в язык, используются более простые языковые конструкции для определения поведения.
Недавно я прочитал мнение о том, что Go – не плохой язык, он просто недостаточно хороший. После того как я последний месяц занимался Rust, моё первое впечатление от Swift было очень схожим.
Фух. Есть нечто, что я теперь ценю в Rust, Haskell и других языках: между неявно/автоматически импортированным набором стандартных функций и наличием действительно глобальных функций есть разница. Существуют ли на самом деле в Swift функции, такие как print в глобальном пространстве имён, что, кажется, подразумевается в книге, или же они импортируются автоматически, как в Rust/Haskell и т.д.?
Поправка: Swift действует так же, но вы не можете обратиться к соответствующему модулю напрямую. Выглядит как половинчатое решение.
Хм… Зачем нужны Double и Float? Неужели только для взаимодействия с Objective-C?
Поправка: друг подсказал мне, что существуют 32 и 64-битные архитектуры. Иногда вы просто не хотите использовать 64-битные числа с плавающей точкой. Кстати, Rust тоже присутствуют два отдельных типа: f32 и f64.
В продолжение разговора про классы, структуры и протоколы: разница в подходе справедлива и для методов-расширения (extension method), которые не тождественны реализации протокола. В Rust же и то и другое решается одной конструкцией — impl. Это не потому, что impl перегружен, а потому, что лежащие в основе языковые механизмы работают одинаково.
(Чувствую, что изучение Swift делает меня ещё большим фанатом Rust.)
Параллельное чтение двух книг показало себя действительно продуктивным в плане понимания того, как эти два языка подходят к решению одних и тех же проблем. Я никогда не делал ничего подобного, и это увлекательно.
Мне особенно нравится, как в Rust используется точка с запятой для преобразования выражений (expressions) в инструкции (statements), что позволяет легко отличить одно от другого (в том числе, разрешая неявный возврат любого выражения).
Вот другое интересное сравнение: match в Rust и switch/case в Swift используются для сравнения с образцом (pattern matching). Интересно, чем они отличаются. Позволяет ли Swift сравнивать произвольное выражение?
Также я вижу, чем обусловлен выбор синтаксиса в обоих языках, и, хотя я больше склоняюсь к Rust, думаю, что оба варианта довольно неплохи. Синтаксис Swift, по понятным причинам, будет более привычен программистам на С и Objective-C, и это вполне оправданно.
Rust и Swift (II)
Базовые типы и синтаксис, связанный с ними
На первый взгляд, дополнительный синтаксис вокруг опциональных значений больше сбивает с толку, чем помогает. Мне кажется, это вызвано тем, что мне больше нравится принятый в Python подход: «явное лучше неявного» и «должен быть один и желательно только один очевидный вариант решения». Это идёт в разрез с множеством разных способов, которыми можно взаимодействовать с опциональными значениями в Swift. Опциональные типы объявляются двумя способами:
— Оператор "?" создаёт явный опциональный тип, который необходимо проверить тем или иным способом.
— Оператор "!" создаёт «неявный опциональный тип» путём «распаковки» и вызывает ошибку во время исполнения, если опциональное значение пустое.
После создания опционального значения, вы можете взаимодействовать с ним следующим образом:
- используя конструкции if let или while let для получения не нулевого значения.
- используя оператор "!" на имени переменной, который явно «распаковывает» его и вызывает ошибку во время исполнения, если опциональное значение пустое.
В Rust, напротив, вы всегда вынуждены явно использовать метод unwrap или сравнение с образцом. В нём нет неявно распаковываемых типов. Более того, в Rust не существует специального синтаксиса для их создания: они просто объявляются через Option или через другой тип с таким же поведением. try!, сокращающий обработку ошибок, является не специальным синтаксисом, а применением другой стандартной языковой конструкции (в данном случае, макросов).
[Примечание переводчика: с момента написания этой статьи в Rust всё-таки (почти) появился специальный синтаксис-аналог try! — "?", который, впрочем, делает не то же самое, что "!" в Swift. Стоит заметить, что далеко не все приняли это нововведение с радостью. К сожалению, альтернативные варианты предполагают наличие типов высшего порядка (HKT), которых в Rust пока нет.]
Обсуждение assert в руководстве по Swift снова поднимает вопрос о глобальном пространстве имён: «Для этих случаев применяется глобальная функция assert(_:_:)».
Это еще один признак того, что в Swift на самом деле используется настоящее глобальное пространство имён, а не автоматический импорт, что может иметь большое значение в определенных случаях, например в системном программировании, когда у вас возникнет обоснованное желание заменить стандартную библиотеку на другую. (Смотрите #[no_std] в документации Rust и RFC по этой теме.
Поправка: из надежных источников я узнал, что был неправ, и я рад этому. Как и в Haskell, эти функции неявно импортированы и входят в модуль Swift.
В Rust assert! является не функцией, а макросом: данное различие вызывает интерес, но не имеет принципиального значения в данном случае. (Хотя может и иметь, мне нужно увидеть реализацию обоих, чтобы понять, чем они отличаются).
В любом случае, это подчеркивает ещё одно значительное различие: в Rust тестирование занимает центральное место, а в руководстве по Swift практически не упоминается (и не вынесено в отдельную главу в содержании). Поддержка тестирования на языковом уровне очень важна.
Прочитав вступление и первую часть руководства по Swift, я полагаю, что, хотя этот язык намного больше подходит для разработки приложений, чем С или С++ (и, предположительно, Objective C, но поскольку я его не знаю, судить не могу), Rust всё равно лучше. Оба языка более современны, нежели их предшественники, однако одни и те же проблемы в них решаются удивительно различными способами, несмотря на похожий синтаксис. Пока что подход Rust мне нравится больше.
В частности, я предпочитаю не добавлять особый синтаксис на каждый отдельный случай. Предоставление хороших языковых конструкций и примитивов, на которых строится всё остальное, кажется лучшим по многим причинам:
- Существенно уменьшается когнитивная нагрузка на разработчика за счёт минимума конструкций, которые применяются по-разному.
- Возрастает качество этих примитивов, поскольку они должны покрывать все случаи.
- Это позволяет разработчикам подходить к решению проблемы неожиданным для создателей языка способом. Со временем сообщество может выработать соглашения по улучшению подхода в стандартной библиотеке, а в самом языке (или компиляторе!) даже не придётся ничего менять.
- В общем, это упрощает внесение изменений, и они могут быть инициированы скорее сообществом, а не необходимостью вмешательства со стороны разработчиков языка. Впрочем, Apple могли сознательно принять такое решение, ведь жёсткий контроль над инструментами разработки вполне в их духе.
Автор: DarkEld3r