ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 11.01.2024
Просмотров: 1141
Скачиваний: 5
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Сигнализация потокам прекратить прослушивание получения задач
Теперь со всеми внесёнными нами изменениями код компилируется без каких-либо предупреждений. Но плохая новость в том, что этот код ещё не работает так, как мы этого хотим. Ключом является логика в замыканиях, запускаемых потоками экземпляров
Worker
. В данный момент мы вызываем join
, но это не завершит потоки, потому что они работают в цикле loop бесконечно в поиске новой задачи (job). Если мы попытаемся удалить
ThreadPool в текущей реализации drop
, то основной поток навсегда заблокируется в ожидании завершения первого потока из пула.
Для решения этой проблемы, мы изменим потоки так, чтобы они прослушивали либо задачи
Job для её выполнения, либо сигнал, что они должны прекратить прослушивание и выйти из бесконечного цикла. Вместо отправки экземпляров задач
Job
, наш канал отправит один из этих двух вариантов перечисления.
Файл: src/lib.rs
Это перечисление
Message будет либо вариантом
NewJob
, который внутри держит
Job с
потоком для выполнения, или это будет вариант
Terminate
, который сделает так, чтобы поток вышел из цикла и остановился.
Нам нужно настроить канал для использования значений типа
Message
, а не типа
Job как показано в листинге 20-23.
Файл: src/lib.rs impl
Drop for
ThreadPool { fn drop
(&
mut self
) { for worker in
&
mut self
.workers { println!
(
"Shutting down worker {}"
, worker.id); if let
Some
(thread) = worker.thread.take() { thread.join().unwrap();
}
}
}
}
{{#rustdoc_include ../listings/ch20-web-server/no-listing-
07
-define-message- enum
/
src
/lib.rs:here}}
Листинг 20-23: Отправка и получение значений перечисления
Message
и выход из цикла, если
Worker
получает перечисление
Message::Terminate
Чтобы встроить в код перечисление
Message
, нужно изменить
Job на
Message в двух местах: в объявлении
ThreadPool и сигнатуре
Worker::new
. Метод execute у
ThreadPool должен отправлять задания, завёрнутые в вариант
Message::NewJob
. Затем в
Worker::new
, где
Message получен из канала, задание (job) будет обработано, если получен вариант
NewJob и поток выйдет из цикла, если получен вариант
Terminate
С такими изменениями код компилируется и продолжит функционировать так же, как и после кода 20-20. Но мы получим предупреждение, потому что мы не создаём сообщения типа
Terminate
. Давайте исправим это предупреждение, изменив нашу реализацию
Drop как в листинге 20-25.
pub struct
ThreadPool
{ workers:
Vec
, sender:
Option
<:sender>>,
}
// --snip-- impl
ThreadPool { pub fn new
(size: usize
) -> ThreadPool {
// --snip--
ThreadPool { workers, sender:
Some
(sender),
}
} pub fn execute
(&
self
, f: F) where
F:
FnOnce
() +
Send
+
'static
,
{ let job =
Box
::new(f); self
.sender.as_ref().unwrap().send(job).unwrap();
}
} impl
Drop for
ThreadPool { fn drop
(&
mut self
) { drop
(
self
.sender.take()); for worker in
&
mut self
.workers { println!
(
"Shutting down worker {}"
, worker.id); if let
Some
(thread) = worker.thread.take() { thread.join().unwrap();
}
}
}
}
Файл: src/lib.rs
Листинг 20-24: Отправка
Message::Terminate
рабочим потокам перед вызовом
join
в каждом потоке
Теперь мы дважды проходим по потокам "работников": один раз для отправки одного сообщения
Terminate каждому потоку и один раз для вызова join у каждого рабочего потока. Если бы мы попытались отправить сообщение и сразу же выполнить join в этом же цикле, мы не смогли бы гарантировать, что рабочий поток в текущей итерации получит сообщение из канала.
Чтобы лучше понять, почему нужны два отдельных цикла, представьте сценарий с двумя работниками. Если бы мы использовали один цикл для перебора каждого работника, на первой итерации сообщение о прекращении работы было бы отправлено в канал и метод join вызывался бы у первого рабочего потока. Если этот первый поток занят обработкой запроса в данный момент, второй рабочий поток получит сообщение о завершении из канала и завершит свою работу. В главном потоке мы бы остались в ожидании завершения работы первого рабочего потока, но этого не произошло, потому что второй поток получил сообщение о завершении. Это ситуация взаимной блокировки
(deadlock)!
Чтобы предотвратить такой сценарий, мы сначала помещаем все сообщения
Terminate в канал в первом цикле; затем мы объединяем (join) для завершения все потоки во втором цикле. Каждый рабочий поток прекратит получать запросы из канала, как только получит сообщение о завершении. Таким образом, мы можем быть уверены, что если мы отправим количество завершающих сообщений равное количеству рабочих потоков то,
каждый получит сообщение о завершении до вызова join в его потоке.
impl
Worker { fn new
(id: usize
, receiver: Arc>>) -> Worker { let thread = thread::spawn(
move
|| loop
{ match receiver.lock().unwrap().recv() {
Ok
(job) => { println!
(
"Worker {id} got a job; executing."
); job();
}
Err
(_) => { println!
(
"Worker {id} disconnected; shutting down."
); break
;
}
}
});
Worker { id, thread:
Some
(thread),
}
}
}
Чтобы увидеть этот код в действии, давайте изменим main
, чтобы принимать только два запроса, прежде чем корректно завершить работу сервера как показано в листинге 20-
25.
Файл: src/bin/main.rs
Код 20-25. Выключение сервера после обслуживания двух запросов с помощью выхода из цикла
Вы бы не хотели, чтобы реальный веб-сервер отключался после обслуживания только двух запросов. Этот код всего лишь демонстрирует, что корректное завершение работы и освобождение ресурсов находятся в рабочем состоянии.
Метод take определён в типаже
Iterator и ограничивает итерацию максимум первыми двумя элементами.
ThreadPool выйдет из области видимости в конце main и будет запущена его реализация drop
Запустите сервер с cargo run и сделайте три запроса. Третий запрос должен выдать ошибку и в терминале вы должны увидеть вывод, подобный следующему:
Вы возможно увидите другой порядок рабочих потоков и напечатанных сообщений. Мы можем увидеть, как этот код работает по сообщениям: "работники" номер 0 и 3 получили первые два запроса, затем на третьем запросе сервер прекратил принимать соединения.
Когда
ThreadPool выходит из области видимости в конце main
, то срабатывает его реализация типажа
Drop и пул сообщает всем рабочим потокам прекратить выполнение. Каждый рабочий поток распечатывает сообщение, когда видит сообщение о завершении, а затем пул потоков вызывает join
, чтобы завершить работу каждого рабочего потока.
{{#rustdoc_include ../listings/ch20-web-server/listing-
20
-
25
/src/bin/main.rs:here}}
$
cargo run
Compiling hello v0.1.0 (file:///projects/hello)
Finished dev [unoptimized + debuginfo] target(s) in 1.0s
Running `target/debug/main`
Worker 0 got a job; executing.
Worker 3 got a job; executing.
Shutting down.
Sending terminate message to all workers.
Shutting down all workers.
Shutting down worker 0
Worker 1 was told to terminate.
Worker 2 was told to terminate.
Worker 0 was told to terminate.
Worker 3 was told to terminate.
Shutting down worker 1
Shutting down worker 2
Shutting down worker 3
Обратите внимание на один интересный аспект этого конкретного выполнения:
ThreadPool отправил сообщения о завершении в канал и прежде чем какой-либо рабочий получил сообщение, мы пытались присоединить (join) "работника" с номером 0.
Рабочий поток 0 ещё не получил сообщение о прекращении, поэтому основной поток заблокировал ожидание потока 0 для завершения. Тем временем каждый из рабочих потоков получил сообщения об завершении. Когда рабочий поток 0 завершился,
основной поток ждал окончания завершения выполнения остальных рабочих потоков. В
этот момент все они получили сообщение о завершении и смогли завершиться.
Поздравления! Теперь мы завершили проект; у нас есть базовый веб-сервер, который использует пул потоков для асинхронных ответов. Мы можем выполнить корректное завершение работы сервера, который очищает все потоки в пуле.
Вот полный код для справки:
Файл: src/bin/main.rs
Файл: src/lib.rs
В коде можно сделать больше! Если вы хотите продолжить совершенствование этого проекта, вот несколько идей:
Добавьте больше документации в
ThreadPool и его публичным методам.
Добавьте тесты функционалу из библиотеки.
Заменить вызовы unwrap на более правильную обработку ошибок.
Используйте
ThreadPool для выполнения некоторых других задач, помимо обслуживания веб-запросов.
Найдите крейт для пула потоков на crates.io и реализуйте аналогичный веб-сервер,
используя такой крейт. Затем сравните его API и надёжность с пулом потоков,
который мы реализовали.
Итоги
Отличная работа! Вы сделали это к концу книги! Мы хотим поблагодарить вас за то, что присоединились к нам в этом путешествии по языку Rust. Теперь вы готовы реализовать свои собственные проекты на Rust и помочь с проектами другим людям. Имейте в виду,
что существует приветливое сообщество других Rust разработчиков, которые хотели бы помочь вам с любыми сложными задачами с которыми вы столкнётесь в своём Rust путешествии.
{{#rustdoc_include ../listings/ch20-web-server/no-listing-
08
- final
- code/src/bin/main.rs}}
{{#rustdoc_include ../listings/ch20-web-server/no-listing-
08
- final
- code/src/lib.rs}}
Дополнительная информация
Следующие разделы содержат справочные материалы, которые могут оказаться полезными в вашем путешествии по Rust.
Приложение A: Ключевые слова
Следующий список содержит ключевые слова, зарезервированные для текущего или будущего использования в языке Rust. Как таковые их нельзя использовать в качестве идентификаторов (за исключением сырых идентификаторов, которые мы обсудим в разделе
«Сырые идентификаторы»
). Идентификаторы — это имена функций,
переменных, параметров, полей структур, модулей, крейтов, констант, макросов,
статических значений, атрибутов, типов, свойств или времён жизни.
Ключевые слова, использующиеся в Rust в настоящее время
Ниже приведён список используемых в настоящее время ключевых слов с описанием их функций.
as
— выполнить примитивное преобразование, уточнить конкретную характеристику, которую содержит объект, или переименовать элемент в выражении use async
— вернуть
Future вместо блокирования текущего потока await
— приостановить выполнение до тех пор, пока не будет готов результат
Future break
— немедленно завершить цикл const
— определить константу или неизменяемый указатель continue
— досрочно перейти к следующей итерации цикла crate
— в пути модуля ссылается на корень пакета dyn
— динамическая отсылка к объекту характеристики else
— ветвь для конструкций потока управления if и if let в случае, если никакая другая ветвь не была исполнена enum
— определить перечисление extern
— подключить внешнюю функцию или переменную false
— логический литерал «ложь»
fn
— определить функцию или тип указателя на функцию for
— перебор элементов итератора, реализация характеристики или указание срока жизни более продолжительного периода if
— ветвление на основе результата условного выражения impl
— реализовать функциональность непосредственно или через характеристику in
— часть синтаксиса определения цикла for let
— объявить переменную loop
— безусловный (бесконечный) цикл match
— сопоставить значение с образцами mod
— определить модуль move
— заставить замыкание принять на себя владение всеми своими захватами
Сырые идентификаторы — это синтаксис, позволяющий использовать ключевые слова там, где обычно они не могут быть. Для создания и использования сырого идентификатора к ключевому слову добавляется префикс r#
Например, ключевое слово match
. Если вы попытаетесь скомпилировать следующую функцию, использующую в качестве имени match
:
Файл: src/main.rs вы получите ошибку:
Ошибка говорит о том, что вы не можете использовать ключевое слово match в качестве идентификатора функции. Чтобы получить возможность использования слова match в
качестве имени функции, нужно использовать синтаксис «сырых идентификаторов»,
например так:
Файл: src/main.rs
Этот код скомпилируется без ошибок. Обратите внимание, что префикс r#
в определении имени функции указан так же, как он указан в месте её вызова в main
Сырые идентификаторы позволяют вам использовать любое слово, которое вы выберете, в качестве идентификатора, даже если это слово окажется зарезервированным ключевым словом. Это даёт нам больше свободы в выборе имён идентификаторов, а также позволяет нам интегрироваться с программами, написанными на языке, где эти слова не являются ключевыми. Кроме того, необработанные идентификаторы позволяют вам использовать библиотеки, написанные в версии Rust, отличной от используемой в вашем крейте. Например, try не является ключевым словом в выпуске 2015 года, но есть в выпуске 2018 года. Если вы зависите от библиотеки, написанной с использованием версии 2015 года и имеющей функцию try
, вам потребуется использовать синтаксис сырого идентификатора, в данном случае r#try
, для вызова этой функции из кода fn match
(needle: &
str
, haystack: &
str
) -> bool
{ haystack.contains(needle)
} error: expected identifier, found keyword `match`
--> src/main.rs:4:4
|
4 | fn match(needle: &str, haystack: &str) -> bool {
| ^^^^^ expected identifier, found keyword fn r
#
match
(needle: &
str
, haystack: &
str
) -> bool
{ haystack.contains(needle)
} fn main
() { assert!
(r#
match
(
"foo"
,
"foobar"
));
}
Операторы_и_обозначения'>Дополнение Б: Операторы и обозначения
Это дополнение содержит глоссарий синтаксиса Rust, включая операторы и другие обозначения, которые появляются сами по себе или в контексте путей, обобщений,
типажей, макросов, атрибутов, комментариев, кортежей и скобок.
Операторы
Таблица Б-1 содержит операторы языка Rust, пример появления оператора, короткое объяснение, возможность перегрузки оператора. Если оператор можно перегрузить, то показан типаж, с помощью которого его можно перегрузить.
Таблица Б-1: Операторы
Оператор
Пример
Объяснение
Перегружаемость
!
ident!(...)
, ident!{...}
, ident![...]
Вызов макроса
!
!expr
Побитовое или логическое отрицание
Not
!=
expr != expr
Сравнение "не равно"
PartialEq
%
expr % expr
Остаток от деления
Rem
%=
var %= expr
Остаток от деления и присваивание
RemAssign
&
&expr
,
&mut expr
Заимствование
&
&type
,
&mut type
,
&'a type
,
&'a mut type
Указывает что данный тип заимствуется
&
expr & expr
Побитовое И
BitAnd
&=
var &= expr
Побитовое И и присваивание
BitAndAssign
&&
expr && expr
Логическое И
*
expr * expr
Арифметическое умножение
Mul
*=
var *= expr
Арифметическое умножение и присваивание
MulAssign
Оператор
Пример
Объяснение
Перегружаемость
*
*expr
Разыменование ссылки
Deref
*
*const type
,
*mut type
Указывает, что данный тип является сырым указателем
+
trait + trait
,
'a + trait
Соединение ограничений типа
+
expr + expr
Арифметическое сложение
Add
+=
var += expr
Арифметическое сложение и присваивание
AddAssign
,
expr, expr
Аргумент и разделитель элементов
-
- expr
Арифметическое отрицание
Neg
- expr - expr
Арифметическое вычитание
Sub
- var -= expr
Арифметическое вычитание и присваивание
SubAssign
->
fn(...) -> type
,
|...|
-> type expr.ident
Доступ к элементу
, expr..
,
..expr
, expr..expr
Указывает на диапазон чисел,
исключая правый
PartialOrd
..=
..=expr
, expr..=expr
Указывает на диапазон чисел,
включая правый
PartialOrd
..expr
Синтаксис обновления структуры variant(x, ..)
,
struct_type { x, .. }
Привязка «И все остальное»
Теперь со всеми внесёнными нами изменениями код компилируется без каких-либо предупреждений. Но плохая новость в том, что этот код ещё не работает так, как мы этого хотим. Ключом является логика в замыканиях, запускаемых потоками экземпляров
Worker
. В данный момент мы вызываем join
, но это не завершит потоки, потому что они работают в цикле loop бесконечно в поиске новой задачи (job). Если мы попытаемся удалить
ThreadPool в текущей реализации drop
, то основной поток навсегда заблокируется в ожидании завершения первого потока из пула.
Для решения этой проблемы, мы изменим потоки так, чтобы они прослушивали либо задачи
Job для её выполнения, либо сигнал, что они должны прекратить прослушивание и выйти из бесконечного цикла. Вместо отправки экземпляров задач
Job
, наш канал отправит один из этих двух вариантов перечисления.
Файл: src/lib.rs
Это перечисление
Message будет либо вариантом
NewJob
, который внутри держит
Job с
потоком для выполнения, или это будет вариант
Terminate
, который сделает так, чтобы поток вышел из цикла и остановился.
Нам нужно настроить канал для использования значений типа
Message
, а не типа
Job как показано в листинге 20-23.
Файл: src/lib.rs impl
Drop for
ThreadPool { fn drop
(&
mut self
) { for worker in
&
mut self
.workers { println!
(
"Shutting down worker {}"
, worker.id); if let
Some
(thread) = worker.thread.take() { thread.join().unwrap();
}
}
}
}
{{#rustdoc_include ../listings/ch20-web-server/no-listing-
07
-define-message- enum
/
src
/lib.rs:here}}
Листинг 20-23: Отправка и получение значений перечисления
Message
и выход из цикла, если
Worker
получает перечисление
Message::Terminate
Чтобы встроить в код перечисление
Message
, нужно изменить
Job на
Message в двух местах: в объявлении
ThreadPool и сигнатуре
Worker::new
. Метод execute у
ThreadPool должен отправлять задания, завёрнутые в вариант
Message::NewJob
. Затем в
Worker::new
, где
Message получен из канала, задание (job) будет обработано, если получен вариант
NewJob и поток выйдет из цикла, если получен вариант
Terminate
С такими изменениями код компилируется и продолжит функционировать так же, как и после кода 20-20. Но мы получим предупреждение, потому что мы не создаём сообщения типа
Terminate
. Давайте исправим это предупреждение, изменив нашу реализацию
Drop как в листинге 20-25.
pub struct
ThreadPool
{ workers:
Vec
Option
<:sender>>,
}
// --snip-- impl
ThreadPool { pub fn new
(size: usize
) -> ThreadPool {
// --snip--
ThreadPool { workers, sender:
Some
(sender),
}
} pub fn execute
self
, f: F) where
F:
FnOnce
() +
Send
+
'static
,
{ let job =
Box
::new(f); self
.sender.as_ref().unwrap().send(job).unwrap();
}
} impl
Drop for
ThreadPool { fn drop
(&
mut self
) { drop
(
self
.sender.take()); for worker in
&
mut self
.workers { println!
(
"Shutting down worker {}"
, worker.id); if let
Some
(thread) = worker.thread.take() { thread.join().unwrap();
}
}
}
}
Файл: src/lib.rs
Листинг 20-24: Отправка
Message::Terminate
рабочим потокам перед вызовом
join
в каждом потоке
Теперь мы дважды проходим по потокам "работников": один раз для отправки одного сообщения
Terminate каждому потоку и один раз для вызова join у каждого рабочего потока. Если бы мы попытались отправить сообщение и сразу же выполнить join в этом же цикле, мы не смогли бы гарантировать, что рабочий поток в текущей итерации получит сообщение из канала.
Чтобы лучше понять, почему нужны два отдельных цикла, представьте сценарий с двумя работниками. Если бы мы использовали один цикл для перебора каждого работника, на первой итерации сообщение о прекращении работы было бы отправлено в канал и метод join вызывался бы у первого рабочего потока. Если этот первый поток занят обработкой запроса в данный момент, второй рабочий поток получит сообщение о завершении из канала и завершит свою работу. В главном потоке мы бы остались в ожидании завершения работы первого рабочего потока, но этого не произошло, потому что второй поток получил сообщение о завершении. Это ситуация взаимной блокировки
(deadlock)!
Чтобы предотвратить такой сценарий, мы сначала помещаем все сообщения
Terminate в канал в первом цикле; затем мы объединяем (join) для завершения все потоки во втором цикле. Каждый рабочий поток прекратит получать запросы из канала, как только получит сообщение о завершении. Таким образом, мы можем быть уверены, что если мы отправим количество завершающих сообщений равное количеству рабочих потоков то,
каждый получит сообщение о завершении до вызова join в его потоке.
impl
Worker { fn new
(id: usize
, receiver: Arc
move
|| loop
{ match receiver.lock().unwrap().recv() {
Ok
(job) => { println!
(
"Worker {id} got a job; executing."
); job();
}
Err
(_) => { println!
(
"Worker {id} disconnected; shutting down."
); break
;
}
}
});
Worker { id, thread:
Some
(thread),
}
}
}
Чтобы увидеть этот код в действии, давайте изменим main
, чтобы принимать только два запроса, прежде чем корректно завершить работу сервера как показано в листинге 20-
25.
Файл: src/bin/main.rs
Код 20-25. Выключение сервера после обслуживания двух запросов с помощью выхода из цикла
Вы бы не хотели, чтобы реальный веб-сервер отключался после обслуживания только двух запросов. Этот код всего лишь демонстрирует, что корректное завершение работы и освобождение ресурсов находятся в рабочем состоянии.
Метод take определён в типаже
Iterator и ограничивает итерацию максимум первыми двумя элементами.
ThreadPool выйдет из области видимости в конце main и будет запущена его реализация drop
Запустите сервер с cargo run и сделайте три запроса. Третий запрос должен выдать ошибку и в терминале вы должны увидеть вывод, подобный следующему:
Вы возможно увидите другой порядок рабочих потоков и напечатанных сообщений. Мы можем увидеть, как этот код работает по сообщениям: "работники" номер 0 и 3 получили первые два запроса, затем на третьем запросе сервер прекратил принимать соединения.
Когда
ThreadPool выходит из области видимости в конце main
, то срабатывает его реализация типажа
Drop и пул сообщает всем рабочим потокам прекратить выполнение. Каждый рабочий поток распечатывает сообщение, когда видит сообщение о завершении, а затем пул потоков вызывает join
, чтобы завершить работу каждого рабочего потока.
{{#rustdoc_include ../listings/ch20-web-server/listing-
20
-
25
/src/bin/main.rs:here}}
$
cargo run
Compiling hello v0.1.0 (file:///projects/hello)
Finished dev [unoptimized + debuginfo] target(s) in 1.0s
Running `target/debug/main`
Worker 0 got a job; executing.
Worker 3 got a job; executing.
Shutting down.
Sending terminate message to all workers.
Shutting down all workers.
Shutting down worker 0
Worker 1 was told to terminate.
Worker 2 was told to terminate.
Worker 0 was told to terminate.
Worker 3 was told to terminate.
Shutting down worker 1
Shutting down worker 2
Shutting down worker 3
Обратите внимание на один интересный аспект этого конкретного выполнения:
ThreadPool отправил сообщения о завершении в канал и прежде чем какой-либо рабочий получил сообщение, мы пытались присоединить (join) "работника" с номером 0.
Рабочий поток 0 ещё не получил сообщение о прекращении, поэтому основной поток заблокировал ожидание потока 0 для завершения. Тем временем каждый из рабочих потоков получил сообщения об завершении. Когда рабочий поток 0 завершился,
основной поток ждал окончания завершения выполнения остальных рабочих потоков. В
этот момент все они получили сообщение о завершении и смогли завершиться.
Поздравления! Теперь мы завершили проект; у нас есть базовый веб-сервер, который использует пул потоков для асинхронных ответов. Мы можем выполнить корректное завершение работы сервера, который очищает все потоки в пуле.
Вот полный код для справки:
Файл: src/bin/main.rs
Файл: src/lib.rs
В коде можно сделать больше! Если вы хотите продолжить совершенствование этого проекта, вот несколько идей:
Добавьте больше документации в
ThreadPool и его публичным методам.
Добавьте тесты функционалу из библиотеки.
Заменить вызовы unwrap на более правильную обработку ошибок.
Используйте
ThreadPool для выполнения некоторых других задач, помимо обслуживания веб-запросов.
Найдите крейт для пула потоков на crates.io и реализуйте аналогичный веб-сервер,
используя такой крейт. Затем сравните его API и надёжность с пулом потоков,
который мы реализовали.
Итоги
Отличная работа! Вы сделали это к концу книги! Мы хотим поблагодарить вас за то, что присоединились к нам в этом путешествии по языку Rust. Теперь вы готовы реализовать свои собственные проекты на Rust и помочь с проектами другим людям. Имейте в виду,
что существует приветливое сообщество других Rust разработчиков, которые хотели бы помочь вам с любыми сложными задачами с которыми вы столкнётесь в своём Rust путешествии.
{{#rustdoc_include ../listings/ch20-web-server/no-listing-
08
- final
- code/src/bin/main.rs}}
{{#rustdoc_include ../listings/ch20-web-server/no-listing-
08
- final
- code/src/lib.rs}}
Дополнительная информация
Следующие разделы содержат справочные материалы, которые могут оказаться полезными в вашем путешествии по Rust.
Приложение A: Ключевые слова
Следующий список содержит ключевые слова, зарезервированные для текущего или будущего использования в языке Rust. Как таковые их нельзя использовать в качестве идентификаторов (за исключением сырых идентификаторов, которые мы обсудим в разделе
«Сырые идентификаторы»
). Идентификаторы — это имена функций,
переменных, параметров, полей структур, модулей, крейтов, констант, макросов,
статических значений, атрибутов, типов, свойств или времён жизни.
Ключевые слова, использующиеся в Rust в настоящее время
Ниже приведён список используемых в настоящее время ключевых слов с описанием их функций.
as
— выполнить примитивное преобразование, уточнить конкретную характеристику, которую содержит объект, или переименовать элемент в выражении use async
— вернуть
Future вместо блокирования текущего потока await
— приостановить выполнение до тех пор, пока не будет готов результат
Future break
— немедленно завершить цикл const
— определить константу или неизменяемый указатель continue
— досрочно перейти к следующей итерации цикла crate
— в пути модуля ссылается на корень пакета dyn
— динамическая отсылка к объекту характеристики else
— ветвь для конструкций потока управления if и if let в случае, если никакая другая ветвь не была исполнена enum
— определить перечисление extern
— подключить внешнюю функцию или переменную false
— логический литерал «ложь»
fn
— определить функцию или тип указателя на функцию for
— перебор элементов итератора, реализация характеристики или указание срока жизни более продолжительного периода if
— ветвление на основе результата условного выражения impl
— реализовать функциональность непосредственно или через характеристику in
— часть синтаксиса определения цикла for let
— объявить переменную loop
— безусловный (бесконечный) цикл match
— сопоставить значение с образцами mod
— определить модуль move
— заставить замыкание принять на себя владение всеми своими захватами
mut
— обозначить изменяемость для ссылок, сырых указателей или привязок к шаблонам pub
— обозначить публичную доступность полей структур, блоков impl или модулей ref
— привязка по ссылке return
— возврат из функции
Self
— псевдоним для типа, который мы определяем или реализуем self
— объект, содержащий этот метод или текущий модуль static
— глобальная переменная или время жизни на протяжении всего выполнения программы struct
— определить структуру super
— родительский модуль текущего модуля trait
— определить характеристику true
— логический литерал «истина»
type
— определить псевдоним типа или ассоциированный тип union
— определение union
; является ключевым словом только при использовании в объявлении союза unsafe
— обозначить небезопасный код, функции, характеристики или реализации use
— ввести объекты в область действия where
— обозначить утверждения, которые ограничивают тип while
— цикл, работающий относительно результата условного выражения
Ключевые слова, зарезервированные для будущего использования
Следующие ключевые слова ещё не имеют никакой функциональности, но зарезервированы Rust для возможного использования в будущем.
abstract become box do final macro override priv try typeof unsized virtual yield
Сырые идентификаторы
— обозначить изменяемость для ссылок, сырых указателей или привязок к шаблонам pub
— обозначить публичную доступность полей структур, блоков impl или модулей ref
— привязка по ссылке return
— возврат из функции
Self
— псевдоним для типа, который мы определяем или реализуем self
— объект, содержащий этот метод или текущий модуль static
— глобальная переменная или время жизни на протяжении всего выполнения программы struct
— определить структуру super
— родительский модуль текущего модуля trait
— определить характеристику true
— логический литерал «истина»
type
— определить псевдоним типа или ассоциированный тип union
— определение union
; является ключевым словом только при использовании в объявлении союза unsafe
— обозначить небезопасный код, функции, характеристики или реализации use
— ввести объекты в область действия where
— обозначить утверждения, которые ограничивают тип while
— цикл, работающий относительно результата условного выражения
Ключевые слова, зарезервированные для будущего использования
Следующие ключевые слова ещё не имеют никакой функциональности, но зарезервированы Rust для возможного использования в будущем.
abstract become box do final macro override priv try typeof unsized virtual yield
Сырые идентификаторы
Сырые идентификаторы — это синтаксис, позволяющий использовать ключевые слова там, где обычно они не могут быть. Для создания и использования сырого идентификатора к ключевому слову добавляется префикс r#
Например, ключевое слово match
. Если вы попытаетесь скомпилировать следующую функцию, использующую в качестве имени match
:
Файл: src/main.rs вы получите ошибку:
Ошибка говорит о том, что вы не можете использовать ключевое слово match в качестве идентификатора функции. Чтобы получить возможность использования слова match в
качестве имени функции, нужно использовать синтаксис «сырых идентификаторов»,
например так:
Файл: src/main.rs
Этот код скомпилируется без ошибок. Обратите внимание, что префикс r#
в определении имени функции указан так же, как он указан в месте её вызова в main
Сырые идентификаторы позволяют вам использовать любое слово, которое вы выберете, в качестве идентификатора, даже если это слово окажется зарезервированным ключевым словом. Это даёт нам больше свободы в выборе имён идентификаторов, а также позволяет нам интегрироваться с программами, написанными на языке, где эти слова не являются ключевыми. Кроме того, необработанные идентификаторы позволяют вам использовать библиотеки, написанные в версии Rust, отличной от используемой в вашем крейте. Например, try не является ключевым словом в выпуске 2015 года, но есть в выпуске 2018 года. Если вы зависите от библиотеки, написанной с использованием версии 2015 года и имеющей функцию try
, вам потребуется использовать синтаксис сырого идентификатора, в данном случае r#try
, для вызова этой функции из кода fn match
(needle: &
str
, haystack: &
str
) -> bool
{ haystack.contains(needle)
} error: expected identifier, found keyword `match`
--> src/main.rs:4:4
|
4 | fn match(needle: &str, haystack: &str) -> bool {
| ^^^^^ expected identifier, found keyword fn r
#
match
(needle: &
str
, haystack: &
str
) -> bool
{ haystack.contains(needle)
} fn main
() { assert!
(r#
match
(
"foo"
,
"foobar"
));
}
версии 2018 года. См.
Приложение E
для получения дополнительной информации о редакциях Rust.
Приложение E
для получения дополнительной информации о редакциях Rust.
Операторы_и_обозначения'>Дополнение Б: Операторы и обозначения
Это дополнение содержит глоссарий синтаксиса Rust, включая операторы и другие обозначения, которые появляются сами по себе или в контексте путей, обобщений,
типажей, макросов, атрибутов, комментариев, кортежей и скобок.
Операторы
Таблица Б-1 содержит операторы языка Rust, пример появления оператора, короткое объяснение, возможность перегрузки оператора. Если оператор можно перегрузить, то показан типаж, с помощью которого его можно перегрузить.
Таблица Б-1: Операторы
Оператор
Пример
Объяснение
Перегружаемость
!
ident!(...)
, ident!{...}
, ident![...]
Вызов макроса
!
!expr
Побитовое или логическое отрицание
Not
!=
expr != expr
Сравнение "не равно"
PartialEq
%
expr % expr
Остаток от деления
Rem
%=
var %= expr
Остаток от деления и присваивание
RemAssign
&
&expr
,
&mut expr
Заимствование
&
&type
,
&mut type
,
&'a type
,
&'a mut type
Указывает что данный тип заимствуется
&
expr & expr
Побитовое И
BitAnd
&=
var &= expr
Побитовое И и присваивание
BitAndAssign
&&
expr && expr
Логическое И
*
expr * expr
Арифметическое умножение
Mul
*=
var *= expr
Арифметическое умножение и присваивание
MulAssign
Оператор
Пример
Объяснение
Перегружаемость
*
*expr
Разыменование ссылки
Deref
*
*const type
,
*mut type
Указывает, что данный тип является сырым указателем
+
trait + trait
,
'a + trait
Соединение ограничений типа
+
expr + expr
Арифметическое сложение
Add
+=
var += expr
Арифметическое сложение и присваивание
AddAssign
,
expr, expr
Аргумент и разделитель элементов
-
- expr
Арифметическое отрицание
Neg
- expr - expr
Арифметическое вычитание
Sub
- var -= expr
Арифметическое вычитание и присваивание
SubAssign
->
fn(...) -> type
,
|...|
-> type expr.ident
Доступ к элементу
, expr..
,
..expr
, expr..expr
Указывает на диапазон чисел,
исключая правый
PartialOrd
..=
..=expr
, expr..=expr
Указывает на диапазон чисел,
включая правый
PartialOrd
..expr
Синтаксис обновления структуры variant(x, ..)
,
struct_type { x, .. }
Привязка «И все остальное»
1 ... 54 55 56 57 58 59 60 61 62