Первый опыт Rust-разработчика с Go

Go

Введение автора: Ник Кэмерон, инженер отдела исследований и разработок PingCAP, основной участник языка Rust.

Спасибо партнерам китайского сообщества языка Rust за перевод и рецензирование:

  • Перевод: Шан Чжоран
  • Рецензенты: У Цун, Чжан Ханьдун

Последние несколько недель я пишу программы на Go. Я впервые использую Go в большом и важном проекте. Изучая возможности Rust, я также много читал о Go, в том числе экспериментировал с примерами и писал игрушечные программы. Но на самом деле программирование с его помощью — это совсем другой опыт.

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

общее впечатление

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

По сравнению с C/C++, Java, Python и т. д., у Go не так много проблемных мест, и он более продуктивен. Однако он все еще находится в той же эпохе, что и эти языки. Он определенно принадлежит тому поколению, хотя он извлек некоторые уроки из других языков, и даже я лично думаю, что он может быть лучшим из этого поколения. Это постепенное улучшение, а не новое (чтобы было ясно, это не предназначено для критики его ценности, с точки зрения разработки программного обеспечения, дополнительные улучшения обычно хороши). Хороший примерnil: Такие языки, как Rust и Swift, убрали концепцию null и устранили целый класс связанных ошибок. Go снижает часть риска: no nullvalues),существуетnilи0различать. Но основная идея остается прежней, и также возникает распространенная ошибка времени выполнения, связанная с разыменованием нулевого указателя.

обучаемость

Го очень легко научиться. Я знаю, что люди часто рекламируют это, но я действительно поражен тем, как быстро возросла моя продуктивность. Благодаря языку Go, его документации и инструментам мне потребовалось всего два дня, чтобы написать «достойный» коммитируемый код.

На способность к обучению влияют следующие факторы:

  • Го худой. Многие языки пытаются выглядеть маленькими, но Go действительно это делает (что в принципе хорошо, я впечатлен самодисциплиной).

  • Стандартная библиотека отличная (опять же маленькая). Очень легко найти и использовать библиотечные программы из экосистемы.

  • Есть несколько вещей, которых нет в других языках. Go берет много вещей из других существующих языков, совершенствует их и, в конце концов, прекрасно объединяет. Он пошел на многое, чтобы не быть нетрадиционным.

утомительный шаблонный код

Код Go может быстро стать очень повторяющимся. Это связано с отсутствием макросов или дженериков как механизма уменьшения дублирования (интерфейсы хороши для абстракции, но не настолько для уменьшения дублирования кода). В итоге я пишу много функций, которые даже идентичны, за исключением типа. Обработка ошибок также может привести к дублированию. во многих функциях, таких какif err != nil { return err }Такого шаблонного кода даже больше, чем реальной ценности. Использование дженериков или макросов для сокращения стандартного кода иногда подвергается критике на том основании, что это не должно делать код менее читаемым, чтобы упростить его написание. Я считаю, что Go представляет собой прямо противоположный пример: копирование и вставка кода часто выполняется быстро и легко, но чтение кода может разочаровать, потому что вам нужно игнорировать много нерелевантного кода или находить тонкие различия во множестве одного и того же кода.

вещи, которые я люблю

  • Время компиляции: Абсолютно быстро, определенно намного быстрее, чем Rust. Но на самом деле это не так быстро, как я ожидал (для средних и крупных проектов я чувствую, что это только близко к C/C++ или немного быстрее. Я больше жду компиляции точно в срок).

  • Горутины и каналы. К чести Go, он предоставляет легкий синтаксис для создания горутин и использования каналов. Несмотря на то, что это небольшая деталь, которая делает опыт параллельного программирования Go лучше других языков, она действительно раскрывает силу синтаксиса.

  • Интерфейсы: они не сложны, но их легко понять и использовать, и они полезны во многих местах.

  • if ...; ... { }Синтаксис: Вы можете ограничить область действия переменных доifФразы действительно хороши. Это то же самое, что и в Swift и Rustif letимеет аналогичный эффект, но более универсален (в Go нет сопоставления с образцом, как в Swift и Rust, поэтому его нельзя использоватьif let).

  • И тесты, и комментарии к документации просты в использовании.

  • Цепочка инструментов Go очень удобна: храните все в одном месте без необходимости использовать несколько инструментов в командной строке.

  • Наличие сборщика мусора (GC): Отсутствие необходимости беспокоиться об управлении памятью действительно упрощает программирование.

  • переменный параметр.

вещи, которые мне не нравятся

Следующее не в определенном порядке.

  • nilНарезка: что нужно знатьnil,nilФрагменты и пустые фрагменты — это не одно и то же, и я уверен, что нам нужны только два из них, а не третий.

  • Типы-перечисления не являются первыми гражданами: эмуляция перечислений с константами кажется шагом назад.

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

  • переключатель позволяет пропустить совпадения.

  • for ... rangeОператор возвращает пару "индекс/значение". Легко получить только индекс (просто проигнорируйте значение), но чтобы получить только значение, вам нужно объявить его явно. На мой взгляд, эта практика должна быть более обратной, так как в большинстве случаев мне нужно значение больше, чем индекс.

  • грамматика:

    • Существует несоответствие между определениями и использованием.
    • Компиляторы иногда бывают привередливы (например, требуют или запрещают запятые в конце); это можно облегчить с помощью хорошего инструментария, но все равно иногда возникают раздражающие дополнительные шаги.
    • При использовании многозначного возвращаемого типа круглые скобки обязательны для типа, ноreturnв предложении не требуется.
    • Объявление структуры требует двух ключевых слов (typeиstruct).
    • Используйте нотацию верхнего регистра для обозначения общедоступных или частных переменных, что похоже на венгерскую нотацию, но хуже.
  • Неявный интерфейс. Я знаю, что это проявляется и в вещах, которые мне нравятся, но иногда это действительно раздражает, особенно когда вы пытаетесь выяснить все типы, которые реализуют этот интерфейс, или какие интерфейсы реализованы для данного типа.

  • Вы не можете писать функции с приемниками в разных пакетах, поэтому, даже если интерфейс «утиный», вы не можете реализовать его для типов в других пакетах, что делает их гораздо менее полезными.

  • И, как я уже упоминал ранее, в Go отсутствуют дженерики и макросы.

последовательность

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

Некоторые вещи, которые меня впечатляют:

  • Синтаксис для возврата нескольких значений и каналов великолепен, но их нельзя использовать вместе, потому что нет типа кортежа.

  • в состоянии использоватьfor ... rangeОператор перебирает массивы и срезы, но ничего не может сделать с другими коллекциями, поскольку в нем отсутствует концепция итераторов.

  • рисунокlenилиappendТакая функция является глобальной функцией, но ваша собственная функция не может быть превращена в глобальную функцию. Эти глобальные функции могут использовать только встроенные типы. Их можно сделать универсальными, даже если в Go «нет дженериков».

  • без перегрузки оператора, то==заставит людей чувствовать себя раздраженными. Потому что это означает, что вы не можете использовать пользовательские типы в качестве ключей в словарях, если они не сопоставимы. Это свойство является производным от структуры типа и не может быть переопределено программистом.

Суммировать

Go — простой, маленький и приятный язык. У него также есть некоторые закоулки и закоулки, но большая часть из них хорошо спроектирована. Он учится с невероятной скоростью и обходит некоторые менее известные особенности других языков.

Go также сильно отличается от Rust. Хотя оба языка можно в общих чертах назвать «системными языками» или «заменителями C», они различаются целями разработки, областями применения, языковыми стилями и приоритетами. Сборка мусора имеет огромное значение: использование GC делает Go проще, меньше и понятнее. Неиспользование GC делает Rust невероятно быстрым (особенно если вам нужна гарантированная задержка, а не только высокая пропускная способность) и включает функции или шаблоны программирования, недоступные в Go (или, по крайней мере, без ущерба для производительности).

Go — компилируемый язык, его среда выполнения хорошо реализована, а его скорость не подлежит сомнению. Rust также является компилируемым языком, но с гораздо меньшим временем выполнения он очень быстр. В отсутствие других ограничений я думаю, что выбор между Go или Rust на самом деле означает компромисс:

  • С одной стороны, у Go более короткая кривая обучения и более простые программы (что означает более быструю разработку);

  • Rust, с другой стороны, действительно производительный и имеет более выразительную систему типов (что делает программы более безопасными, что также означает более быструю отладку и поиск ошибок).

Прикрепил:Оригинальная статья на английском языке

Вы можете оставить сообщение ниже и рассказать о своем опыте Go & Rust: от входа до отказа, от отказа до мастерства, ямы, на которые наступили, слезы, которые текли ... Или расскажите об их must-kill преимущества.

⚠️ Интеллектуальная дискуссия, Мир и Любовь.