Конкурентность без страха
Безопасное и эффективное конкурентное программирование — ещё одна важная цель Rust. Конкурентное программирование (concurrent programming), при котором разные части программы выполняются независимо, и параллельное программирование (parallel programming), при котором разные части программы выполняются одновременно, становятся всё важнее: всё больше компьютеров используют несколько процессоров. Исторически программирование в таких условиях было сложным и чреватым ошибками. Rust надеется это изменить.
Поначалу команда Rust считала, что обеспечение безопасности памяти и предотвращение проблем конкурентности — две разные задачи, которые решаются разными методами. Со временем выяснилось, что системы владения и типов — мощный набор инструментов, помогающий справиться и с безопасностью памяти, и с проблемами конкурентности! Благодаря владению и проверке типов многие ошибки конкурентности в Rust — это ошибки компиляции, а не ошибки времени выполнения. Поэтому вместо того, чтобы тратить массу времени, пытаясь воспроизвести точные условия, при которых возникает баг конкурентности, вы получаете код, который просто не компилируется, и сообщение об ошибке с объяснением проблемы. В итоге код можно исправить, пока вы над ним работаете, а не когда он, возможно, уже ушёл в продакшен. Эту особенность Rust прозвали конкурентностью без страха (fearless concurrency). Она позволяет писать код без коварных багов, который легко рефакторить, не внося новых.
Примечание: для простоты мы будем называть многие проблемы конкурентными, а не уточнять каждый раз «конкурентные и/или параллельные». В этой главе, встречая слово «конкурентный», мысленно читайте «конкурентный и/или параллельный». В следующей главе, где разница важнее, мы будем точнее.
Многие языки догматичны в том, какие решения для конкурентных задач они предлагают. Например, в Erlang есть изящные средства конкурентности на основе передачи сообщений, но лишь малопонятные способы делить состояние между потоками. Поддерживать только часть возможных решений — разумная стратегия для высокоуровневых языков: они обещают выгоду от того, что вы отказываетесь от части контроля ради абстракций. А от низкоуровневых языков ожидают, что в любой ситуации они дадут самое производительное решение и будут меньше абстрагироваться от железа. Поэтому Rust предлагает разные инструменты, чтобы вы могли смоделировать задачу так, как подходит именно вашей ситуации и требованиям.
Вот о чём пойдёт речь в этой главе:
- как создавать потоки, чтобы выполнять несколько фрагментов кода одновременно;
- конкурентность на основе передачи сообщений (message passing), когда потоки обмениваются сообщениями через каналы;
- конкурентность с общим состоянием (shared state), когда у нескольких потоков есть доступ к одним и тем же данным;
- трейты
SyncиSend, которые распространяют гарантии конкурентности Rust на пользовательские типы, а не только на типы стандартной библиотеки.