Когда использовать Rust, а когда - Go

Перевод | Автор оригинала: Michiel Mulders

когда использовать Rust, а когда - Golang

С самого начала есть явные различия между Go и Rust. Go уделяет больше внимания созданию веб-API и небольших сервисов, которые можно масштабировать бесконечно, особенно с помощью горутин. Последнее также возможно с Rust, но с точки зрения разработчика все намного сложнее.

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

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

Если вы только начинаете работать с Rust, было бы неплохо освежить в памяти это руководство для начинающих, прежде чем читать дальше.

Если вы все догнали, давайте нырнем!

Производительность

Первоначально разработанный инженерами Google, Go был представлен публике в 2009 году. Он был создан, чтобы предложить альтернативу C++, более простую в изучении и кодировании и оптимизированную для работы на многоядерных процессорах.

С тех пор Go отлично подходит для разработчиков, которые хотят воспользоваться преимуществами параллелизма, предлагаемыми языком. В языке есть горутины, которые позволяют запускать функции как подпроцессы.

Большим преимуществом Go является то, насколько легко вы можете использовать горутины. Простое добавление синтаксиса go к функции заставляет ее работать как подпроцесс. Модель параллелизма Go позволяет развертывать рабочие нагрузки на нескольких ядрах ЦП, что делает его очень эффективным языком.

package main

import (
    "fmt"
    "time"
)

func f(from string) {
    for i := 0; i < 3; i++ {
        fmt.Println(from, ":", i)
    }
}

func main() {

    f("direct")

    go f("goroutine")
    time.Sleep(time.Second)
    fmt.Println("done")
}

Несмотря на поддержку многоядерных процессоров, Rust все же превосходит Go. Rust более эффективен при выполнении алгоритмов и ресурсоемких операций. Benchmarks Game сравнивает Rust и Go для различных алгоритмов, таких как бинарные деревья. Для всех протестированных алгоритмов Rust был как минимум на 30 процентов быстрее; в случае вычислений двоичного дерева он составлял до 1000 процентов. Исследование Bitbucket показывает аналогичные результаты, в которых Rust работает наравне с C++.

Производительность Rust согласно Bitbucket

(Источник: Benchmarks Game)

Параллелизм

Как упоминалось выше, Go поддерживает параллелизм. Например, предположим, что у вас запущен веб-сервер, который обрабатывает запросы API. Вы можете использовать горутины Go для выполнения каждого запроса как подпроцесса, максимизируя эффективность за счет разгрузки задач на все доступные ядра ЦП.

Горутины являются частью встроенных функций Go, в то время как Rust получил только собственный синтаксис async / await для поддержки параллелизма. Таким образом, когда дело доходит до параллелизма, преимущество разработчиков передается на Go. Однако Rust намного лучше гарантирует безопасность памяти.

Вот пример упрощенных потоков для Rust:

use std::thread;
use std::time::Duration;

fn main() {
   // 1. create a new thread
   for i in 1..10 {
      thread::spawn(|| {
         println!("thread: number {}!", i);
         thread::sleep(Duration::from_millis(100));
      });
   }
  
  println!("hi from the main thread!");
}

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

Rust предлагает четыре различных парадигмы параллелизма, чтобы помочь вам избежать распространенных ошибок безопасности памяти. Мы более подробно рассмотрим две общие парадигмы: канал и блокировка.

Канал

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

Замок

Данные доступны только при удержании блокировки. Rust полагается на принцип блокировки данных вместо кода, который часто встречается в языках программирования, таких как Java.

Дополнительные сведения о концепции владения и всех парадигмах параллелизма можно найти в статье «Бесстрашный параллелизм с Rust».

Безопасность памяти

Ранняя концепция владения - один из главных аргументов в пользу Rust. Rust выводит безопасность типов, что также важно для обеспечения безопасного для памяти параллелизма, на новый уровень.

Согласно блогу Bitbucket, «очень строгий и педантичный компилятор Rust проверяет каждую переменную, которую вы используете, и каждый адрес памяти, на который вы ссылаетесь. Это позволяет избежать возможных состояний гонки данных и информирует вас о неопределенном поведении ».

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

Опыт разработчика

Прежде всего, давайте посмотрим на кривую обучения, связанную с каждым языком. Go был разработан с учетом простоты. Разработчики часто называют его «скучным» языком, что означает, что его ограниченный набор встроенных функций упрощает внедрение Go.

Кроме того, Go предлагает более простую альтернативу C++, скрывающую такие аспекты, как безопасность памяти и распределение памяти. Rust использует другой подход, заставляя задуматься о таких понятиях, как безопасность памяти. Концепция владения и возможность передавать указатели делают Rust менее привлекательным для изучения. Когда вы постоянно думаете о безопасности памяти, вы менее продуктивны, и ваш код неизбежно становится более сложным.

Кривая обучения Rust довольно крутая по сравнению с Go. Однако стоит отметить, что у Go более крутая кривая обучения, чем у более динамичных языков, таких как Python и JavaScript.

Когда использовать Go

Go хорошо работает для самых разных сценариев использования, что делает его отличной альтернативой Node.js для создания веб-API. Как отмечает Лорис Кро, «модель параллелизма Go хорошо подходит для серверных приложений, которые должны обрабатывать несколько независимых запросов». Именно поэтому Go предоставляет горутины.

Более того, Go имеет встроенную поддержку веб-протокола HTTP. Вы можете быстро разработать небольшой API-интерфейс, используя встроенную поддержку HTTP, и запустить его как микросервис. Таким образом, Go хорошо сочетается с архитектурой микросервисов и удовлетворяет потребности разработчиков API.

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

Выберите Go, когда:

Когда использовать Rust

Rust - отличный выбор, когда важна производительность, например, когда вы обрабатываете большие объемы данных. Более того, Rust дает вам детальный контроль над поведением потоков и распределением ресурсов между потоками.

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

Выбирайте Rust, когда:

Go vs Rust: мой честный взгляд

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

Но, в конце концов, какой язык лучше?

Есть много способов подойти к этому вопросу. Я бы порекомендовал подумать, какое приложение вы хотите создать. Go хорошо подходит для создания веб-приложений и API-интерфейсов, которые используют его встроенные функции параллелизма и поддерживают архитектуру микросервисов.

Вы также можете использовать Rust для разработки веб-API, но он не был разработан с учетом этого варианта использования. Сосредоточение внимания Rust на безопасности памяти увеличивает сложность и время разработки, особенно для довольно простого веб-API. Однако больший контроль над кодом позволяет писать более оптимизированный, эффективный с точки зрения памяти и производительный код.