EnglishРусский Map
Rust Send and Sync

Ограничение Sync, о котором никто не просил

title
Ограничение Sync, о котором никто не просил
type
summary
summary
Разбор verrchu: почему &self в асинхронных трейтах с Send-футурами неявно требует Self: Sync и почему &mut self решает проблему
tags
rust, async, type-system
created
2026-05-10
updated
2026-05-10
lang
ru
source_updated
2026-05-10
translated
2026-09-01
translator
lllm/antigravity/gemini-3.7-flash-medium

Первый пост в блоге verrchu разбирает ошибку в асинхронных трейтах Rust, которая выглядит как проблема с Send, но на деле маскирует проблему с Sync. В основе разбора лежит таблица истинности из четырёх ячеек:

impl Send + Sync impl Send + !Sync
футура без ограничения + Send компилируется компилируется
футура с ограничением + Send компилируется ошибка

Падающий вариант (случай 2.2) не компилируется всего из-за двух неочевидных правил Rust:

  • tokio::spawn требует F: Send + 'static, поэтому возвращаемая футура обязана быть Send
  • &T: Send требует T: Sync (тогда как &mut T: Send требует только T: Send)

Поэтому если в трейте объявлен метод fn run(&self) -> impl Future<Output = ()> + Send, захват &self внутри футуры означает, что футура захватывает ссылку &Self. Чтобы этот захват был Send, тип Self обязан реализовывать Sync. Требование Sync всё это время скрывалось внутри &self - стороннее поле с !Sync (Cell<()>) просто делает его явным.

Два способа исправления:

  1. Обернуть поле !Sync в Mutex - работает, потому что для Mutex<T>: Sync требуется только T: Send, но добавляет накладные расходы на блокировку во время выполнения, хотя Self на самом деле никогда не разделяется между потоками
  2. Заменить &self на &mut self - для &mut T: Send требуется только T: Send, поэтому требование Sync полностью исчезает

Второе решение контринтуитивно. В Rust первая интуиция - взять &, потому что это ограничение кажется "строже" (нет мутации). Но с точки зрения трейт-баундов оно оказывается слабее. Настоящий смысл &mut T - уникальная ссылка, а не мутабельная ссылка. Именно уникальность исключает одновременное разделение между потоками, что и снимает требование Sync. (Статья Нико Мацакиса "Focusing on ownership" - канонический текст об &mut как об уникальности.)

Пост короткий, наглядно проиллюстрирован полными текстами ошибок компилятора и служит хорошей отправной точкой для более широкой темы о том, как Send/Sync каскадно влияют на типы возвращаемых значений в асинхронных трейтах. Практический вывод: если метод асинхронного трейта должен возвращать Send-футуру, по умолчанию используйте &mut self, если вам явно не нужен разделяемый доступ.

См. также: rust-send-sync, verrchu-blog, message-passing-vs-shared-memory.