Цель стабилизации: 1.38.0 (бета-версия 15.08.2019)
Это предложение по стабилизации минимально жизнеспособной функции async / await, которая включает:
async
аннотации к функциям и блокам, из-за чего их оценка откладывается и вместо этого оценивается в будущем.await
, действительный только в контексте async
, который принимает в качестве аргумента будущее и заставляет внешнее будущее, в котором оно находится, передавать управление до тех пор, пока ожидаемое будущее не завершится.RFC:
std::task
и std::future::Future
Проблемы с отслеживанием:
Стабилизации:
return
), а не «внешний» тип возвращаемого значения (будущий тип, который оценивается при вызове функции)expression.await
, в отличие от более распространенного await expression
или другого альтернативного синтаксиса.async
и await
настоящее время полагаются на работу TLS. Это проблема реализации, которая не является частью дизайна, и, хотя она не блокирует стабилизацию, предполагается, что в конечном итоге она будет решена.async
как модификатор для закрывающих литералов здесь не стабилизируется. Требуется больше проектных работ в отношении захвата и абстракции асинхронных замыканий со сроками жизни.Обработка неблокирующего ввода-вывода очень важна для разработки высокопроизводительных сетевых сервисов, что является целевым вариантом использования Rust, вызывающим значительный интерес со стороны производственных пользователей. По этой причине решение, позволяющее сделать эргономичное и выполнимое написание сервисов с использованием неблокирующего ввода-вывода, долгое время было целью Rust. Функция async / await - это кульминация этих усилий.
До версии 1.0 в Rust была система greenthreading, в которой Rust предоставлял альтернативный примитив многопоточности на уровне языка, построенный поверх неблокирующего ввода-вывода. Однако эта система вызвала несколько проблем: наиболее важно введение языковой среды выполнения, которая влияла на производительность даже программ, которые ее не использовали, значительно увеличивая накладные расходы на FFI, а также наличие нескольких серьезных нерешенных проблем проектирования, связанных с реализацией стеков greenthread. .
После удаления зеленых нитей участники проекта Rust начали работу над альтернативным решением, основанным на абстракции фьючерсов. Иногда также называемые обещаниями, фьючерсы были очень успешными в других языках в качестве абстракции на основе библиотек для неблокирующего ввода-вывода, и было известно, что в долгосрочной перспективе они хорошо отображаются на синтаксис async / await, который может сделать их лишь незначительно менее удобными, чем полностью невидимая система пропитки.
Основным прорывом в развитии абстракции Future стало введение модели Futures, основанной на опросах. В то время как другие языки используют модель, основанную на обратном вызове, в которой само будущее отвечает за планирование выполнения обратного вызова после его завершения, Rust использует модель на основе опроса, в которой исполнитель отвечает за опрос будущего до завершения, а future просто информирует исполнителя о том, что он готов к дальнейшему прогрессу, используя абстракцию Waker. Эта модель хорошо себя зарекомендовала по нескольким причинам:
(Последние два пункта также были определены как источник путаницы для пользователей, переходящих с других языков, на которых они не соответствуют действительности, и приносящих с собой ожидания от этих языков. Однако оба эти свойства являются неизбежными свойствами модели на основе опросов. который имеет другие очевидные преимущества и, по нашему мнению, станет полезным, если пользователи их поймут.)
Однако модель, основанная на опросах, страдала от серьезных эргономических проблем при взаимодействии со ссылками; по сути, ссылки в точках доходности приводили к неразрешимым ошибкам компиляции, хотя они и должны быть безопасными. В результате получился сложный, зашумленный код, полный дуг, мьютексов и замыканий перемещений, ни один из которых не был строго необходим. Даже если оставить эту проблему в стороне, без примитива языкового уровня, фьючерсы страдали от принуждения пользователей к стилю написания сильно вложенных обратных вызовов.
По этой причине мы использовали синтаксический сахар async / await с поддержкой нормального использования ссылок в точках доходности. После введения абстракции Pin
которая сделала ссылки на точки доходности безопасными для поддержки, мы разработали собственный синтаксис async / await, который компилирует функции в наши фьючерсы на основе опросов, позволяя пользователям получать преимущества в производительности асинхронного ввода-вывода с Futures при написании кода, который очень похож на стандартный императивный код. Эта последняя особенность является предметом настоящего отчета о стабилизации.
async
Ключевое слово async
может применяться в двух местах:
_ (Другие места для асинхронных функций - например, закрывающие литералы и методы черт будут развиваться и стабилизироваться в будущем.) _
Модификатор async регулирует изменяемый элемент, «превращая его в будущее». В случае блока, блок оценивается с точки зрения будущего его результата, а не его результата. В случае функции вызовы этой функции возвращают будущее ее возвращаемого значения, а не возвращаемое значение. Код внутри элемента, измененного модификатором async, называется находящимся в контексте async.
Модификатор async выполняет эту модификацию, заставляя элемент вместо этого оцениваться как чистый конструктор будущего, принимая аргументы и захваты как поля будущего. Каждая точка ожидания рассматривается как отдельный вариант этого конечного автомата, и метод будущего «опроса» продвигает будущее через эти состояния на основе преобразования кода, написанного пользователем, до тех пор, пока в конечном итоге он не достигнет своего конечного состояния.
async move
Подобно замыканиям, асинхронные блоки могут захватывать переменные в окружающей области видимости в состояние будущего. Как и замыкания, эти переменные по умолчанию фиксируются по ссылке. Однако вместо этого они могут быть захвачены по значению с помощью модификатора move
(точно так же, как замыкания). async
предшествует move
, что делает эти блоки async move { }
блоками.
await
В асинхронном контексте новое выражение может быть сформировано путем объединения выражения с оператором await
, используя следующий синтаксис:
expression.await
Оператор ожидания может использоваться только внутри асинхронного контекста, а тип выражения, к которому он применяется, должен реализовывать черту Future
. Выражение await оценивается как выходное значение будущего, к которому оно применяется.
Оператор await передает управление будущим, которое оценивает асинхронный контекст, до тех пор, пока будущее, к которому он применяется, не завершится. Эту операцию выдачи управления нельзя записать в поверхностном синтаксисе, но если бы она могла (используя синтаксис YIELD_CONTROL!
в этом примере), десугарирование await выглядело бы примерно так:
loop {
match $future.poll(&waker) {
Poll::Ready(value) => break value,
Poll::Pending => YIELD_CONTROL!,
}
}
Это позволяет вам дождаться завершения оценки фьючерсов в асинхронном контексте, перенаправляя передачу управления через Poll::Pending
наружу во внешний асинхронный контекст, в конечном итоге - к исполнителю, на котором было создано будущее.
Наши асинхронные функции и блоки «выходят немедленно» - их построение - это чистая функция, которая переводит их в начальное состояние до выполнения кода в теле асинхронного контекста. Ни один из основных кодов не выполняется, пока вы не начнете опрос этого будущего.
Это отличается от многих других языков, в которых вызовы триггера асинхронной функции запускаются немедленно. В этих других языках async по своей сути является параллельной конструкцией: когда вы вызываете асинхронную функцию, она запускает другую задачу, чтобы начать выполнение одновременно с вашей текущей задачей. В Rust, однако, фьючерсы не выполняются параллельно.
Мы могли бы сделать так, чтобы асинхронные элементы выполнялись до первой точки ожидания, когда они были созданы, вместо того, чтобы делать их чистыми. Однако мы решили, что это больше сбивает с толку: выполняется ли код во время построения будущего или опроса, это будет зависеть от размещения первого await в теле. Проще подумать о том, чтобы весь код выполнялся во время опроса, а не во время построения.
Ссылка:
Синтаксис наших асинхронных функций использует «внутренний» тип возвращаемого значения, а не «внешний» тип возвращаемого значения. То есть они говорят, что возвращают тип, который они в конечном итоге оценивают, вместо того, чтобы говорить, что они возвращают будущее этого типа.
На одном уровне это решение о том, какой вид ясности предпочтительнее: поскольку подпись также включает аннотацию async
, тот факт, что они возвращают будущее, явно указывается в подписи. Однако пользователям может быть полезно увидеть, что функция возвращает будущее, не обращая внимания на ключевое слово async. Но это также похоже на шаблон, поскольку информация также передается с помощью ключевого слова async
.
Что действительно изменило чашу весов для нас, так это вопрос о пожизненной элизии. "Внешний" тип возврата любой асинхронной функции - impl Future<Output = T>
, где T
- это внутренний тип возврата. Однако это будущее также фиксирует время жизни любых входных аргументов само по себе: это противоположно значению по умолчанию для impl Trait, которое, как предполагается, не фиксирует какие-либо входные времена жизни, если вы их не укажете. Другими словами, использование внешнего возвращаемого типа будет означать, что асинхронные функции никогда не выиграют от исключения времени жизни (если мы не сделали что-то еще более необычное, например, чтобы правила исключения времени жизни работали по-разному для асинхронных функций и других функций).
Мы решили, что, учитывая, насколько многословным и откровенно запутанным будет на самом деле писать внешний тип возвращаемого значения, не стоит дополнительно сигнализировать о том, что это возвращает будущее, чтобы потребовать от пользователей его написания.
Порядок деструкторов в асинхронных контекстах такой же, как и в неасинхронных контекстах. Точные правила здесь немного сложны и выходят за рамки, но в целом значения уничтожаются, когда они выходят за рамки. Однако это означает, что они продолжают существовать в течение некоторого времени после использования, пока не будут очищены. Если это время включает операторы ожидания, эти элементы должны быть сохранены в состоянии будущего, чтобы их деструкторы могли запускаться в нужное время.
В качестве оптимизации размера будущих состояний мы могли бы вместо этого переупорядочить деструкторы, чтобы они были более ранними в некоторых или всех контекстах (например, неиспользуемые аргументы функции могут быть немедленно отброшены, а не сохранены в состоянии будущего). Однако мы решили этого не делать. Порядок деструкторов может быть сложной и запутанной проблемой для пользователей и иногда очень важен для семантики программы. Мы решили отказаться от этой оптимизации в пользу обеспечения максимально простого упорядочивания деструкторов - того же порядка деструкторов, если бы все ключевые слова async и await были удалены.
(Когда-нибудь мы можем быть заинтересованы в поиске способов пометить деструкторы как чистые и переупорядочиваемые. Это будущая проектная работа, которая также имеет последствия, не связанные с async / await.)
Ссылка:
Одним из основных отклонений от функций async / await других языков является синтаксис нашего оператора await. Это было предметом огромного количества дискуссий, больше, чем любое другое решение, которое мы приняли при разработке Rust.
С 2015 года в Rust есть постфиксный оператор ?
для эргономичной обработки ошибок. Задолго до 1.0 в Rust также был постфиксный оператор .
для доступа к полям и вызовов методов. Поскольку основным вариантом использования фьючерсов является выполнение некоторого рода операций ввода-вывода, подавляющее большинство фьючерсов оцениваются как Result
с некоторыми
своего рода ошибка. Это означает, что на практике почти каждая операция ожидания выполняется либо с ?
либо с вызовом метода после нее. Учитывая стандартный приоритет для операторов префикса и постфикса, это привело бы к тому, что почти все операторы ожидания были записаны в (await future)?
, что мы считали крайне неэргономичным.
Поэтому мы решили использовать постфиксный синтаксис, который очень хорошо сочетается с операторами ?
и .
. После рассмотрения множества различных синтаксических опций мы решили использовать оператор .
за которым следует ключевое слово await.
Ссылка:
Rust разработан, чтобы упростить написание параллельных и параллельных программ без дополнительных затрат на людей, пишущих программы, которые выполняются в одном потоке. Важно иметь возможность запускать асинхронные функции как на однопоточных, так и на многопоточных исполнителях. Ключевое различие между этими двумя вариантами использования заключается в том, что многопоточные исполнители будут связывать фьючерсы, которые они могут создать, с помощью Send
, а однопоточные исполнители - нет.
Подобно существующему поведению синтаксиса impl Trait
, асинхронные функции «пропускают» автоматические свойства будущего, которые они возвращают. То есть, помимо наблюдения за тем, что внешний тип возвращаемого значения является будущим, вызывающий может также определить, является ли этот тип Send или Sync, на основе исследования его тела. Это означает, что когда тип возвращаемого значения async fn запланирован для многопоточного исполнителя, он может проверить, безопасно ли это. Однако тип не обязательно должен быть Send, поэтому пользователи однопоточных исполнителей могут воспользоваться преимуществами более производительных однопоточных примитивов.
Были некоторые опасения, что это не сработает, когда асинхронные функции будут расширены в методы, но после некоторого обсуждения было определено, что ситуация не будет существенно отличаться.
Ссылка:
Выпуск: # 52924
Способ асинхронного преобразования в конечный автомат в настоящее время реализован не совсем оптимально, в результате чего состояние становится намного больше, чем необходимо. Это возможно, поскольку размер состояния на самом деле увеличивается сверхлинейно, чтобы вызвать переполнение стека в реальном стеке, когда размер состояния становится больше, чем размер обычного системного потока. Улучшение этого кодогенератора так, чтобы размер был более разумным, по крайней мере, не настолько плохим, чтобы вызывать переполнение стека при нормальном использовании, - это исправление ошибки блокировки.
Выпуск: # 56238
Асинхронные функции должны иметь возможность иметь несколько значений времени жизни в своей сигнатуре, все из которых «фиксируются» в будущем, когда функция будет оцениваться при ее вызове. Однако текущее понижение до impl Future
внутри компилятора не поддерживает несколько значений времени жизни ввода; необходим более глубокий рефакторинг, чтобы сделать
эта работа. Поскольку пользователи с большой вероятностью будут писать функции с несколькими (возможно, все исключенными) временами жизни ввода, это исправление блокирующей ошибки.
Все это известные и очень приоритетные расширения для MVP, над которыми мы намерены приступить, как только мы отправим начальную версию async / await.
В первоначальном RFC мы также поддерживали модификатор async в качестве модификатора для литералов замыкания, создавая анонимные асинхронные функции. Однако опыт использования этой функции показал, что есть еще ряд вопросов, которые необходимо решить, прежде чем мы почувствуем себя комфортно, стабилизируя этот вариант использования:
Текущая реализация оператора await требует, чтобы TLS передавал пробуждение вниз, когда он опрашивает внутреннее будущее. По сути, это «взлом», позволяющий как можно скорее заставить синтаксис работать в системах с TLS. В долгосрочной перспективе мы не намерены использовать TLS и предпочли бы передавать пробуждение в качестве обычного аргумента функции. Однако это требует более глубоких изменений в коде генерации конечного автомата, чтобы он мог обрабатывать прием аргументов.
Хотя мы не блокируем внедрение этого изменения, мы считаем его высокоприоритетным, поскольку оно предотвращает использование async / await в системах без поддержки TLS. Это чисто проблема реализации: ничто в дизайне системы не требует использования TLS.
В настоящее время мы не разрешаем использовать асинхронные функции или методы в типажах; это единственное место, где вы можете писать fn
но не async fn
. Очевидно, что асинхронные методы были бы мощной абстракцией, и мы хотим их поддерживать.
Асинхронный метод функционально будет рассматриваться как метод, возвращающий связанный тип, который будет реализовывать future; каждый асинхронный метод будет генерировать уникальный будущий тип для конечного автомата, в который этот метод преобразуется.
Однако, поскольку это будущее будет захватывать все входные данные, любые параметры времени или типа входа также должны быть захвачены в этом состоянии. Это эквивалентно концепции, называемой универсальными связанными типами , функции, которую мы давно хотели, но еще не реализовали должным образом. Таким образом, разрешение асинхронных методов связано с разрешением общих связанных типов.
Есть также нерешенные проблемы с дизайном. Например, взаимозаменяемы ли асинхронные методы с методами, возвращающими будущие типы, которые будут иметь такую же сигнатуру? Кроме того, асинхронные методы представляют дополнительные проблемы, связанные с автоматическими характеристиками, поскольку вам может потребоваться, чтобы будущее, возвращаемое каким-либо асинхронным методом, реализовывало автоматическую характеристику, когда вы абстрагируете по характеристике с помощью асинхронного метода.
Как только мы получим хотя бы эту минимальную поддержку, появятся другие соображения по проектированию будущих расширений, например, возможность сделать асинхронные методы «объектно-безопасными».
У нас есть функция нестабильного генератора, использующая то же преобразование конечного автомата сопрограммы, чтобы принимать функции, которые дают несколько значений, и превращать их в конечный автомат. Наиболее очевидным вариантом использования этой функции является создание функций, которые компилируются в «итераторы», точно так же, как асинхронные функции компилируются в
фьючерсы. Точно так же мы могли бы скомпоновать эти две функции для создания асинхронных генераторов - функций, которые компилируются в «потоки», асинхронный эквивалент итераторов. Для этого есть действительно ясные варианты использования в сетевом программировании, которое часто включает потоки сообщений, отправляемых между системами.
У генераторов есть много открытых вопросов по дизайну, потому что это очень гибкая функция с множеством возможных вариантов. Окончательный дизайн генераторов в Rust с точки зрения синтаксиса и библиотечных API все еще находится в стадии неопределенности.
@rfcbot fcp слияние
Член команды @withoutboats предложил объединить это. Следующим шагом является проверка остальными членами команды, отмеченными тегами:
Обеспокоенность:
После того, как большинство рецензентов одобрит (и не более двух утверждений остаются нерешенными), наступит последний период для комментариев. Если вы заметили серьезную проблему, которая не поднималась ни на одном этапе этого процесса, сообщите об этом!
См. Этот документ для получения информации о том, какие команды могут дать мне члены команды, отмеченные тегами.
(Просто зарегистрируйте существующие блокираторы в отчете выше, чтобы убедиться, что они не соскользнут)
@rfcbot касается реализации-работы-блокировки-стабилизации
Член команды ... предложил объединить это
Как можно объединить проблему Github (а не пулреквест)?
@vi Бот немного глуповат и не проверяет, проблема это или PR :) Здесь вы можете заменить «объединить» на «принять».
Вау, спасибо за исчерпывающее резюме! Я слежу лишь косвенно, но полностью уверен, что вы в курсе всего.
@rfcbot просмотрел
Можно ли явно добавить «Проблемы Triage AsyncAwait-Unclear» к блокираторам стабилизации (и / или зарегистрировать беспокойство по этому поводу)?
У меня есть https://github.com/rust-lang/rust/issues/60414, который я считаю важным (очевидно, это моя ошибка: p), и я хотел бы, по крайней мере, явно отложить его до стабилизации :)
Я просто хотел бы выразить благодарность сообществу за усилия, которые команды Rust вложили в эту функцию! Было много разработок, обсуждений и несколько сбоев в общении, но, по крайней мере, я и, надеюсь, многие другие уверены, что благодаря всему этому мы нашли лучшее решение для Rust. : тада:
(Тем не менее, я хотел бы видеть упоминание о проблемах с мостом к API-интерфейсам системы на основе завершения и асинхронной отмены в будущих возможностях. TL; DR им по-прежнему приходится передавать собственные буферы. Это проблема библиотеки, но одна с упоминанием.)
Я также хотел бы увидеть упоминание о проблемах с API на основе завершения. (см. этот внутренний поток для контекста). Принимая во внимание IOCP и введение io_uring
, который может стать Пути для асинхронного ввода-вывода в Linux, я думаю, что важно иметь четкий путь для их обработки. Идеи гипотетического асинхронного отбрасывания IIUC не могут быть реализованы безопасно, а передача собственных буферов будет менее удобной и потенциально менее производительной (например, из-за худшего местоположения или из-за дополнительных копий).
@newpavlov Я реализовал аналогичные вещи для Fuchsia, и это вполне возможно обойтись без async drop. Для этого есть несколько различных способов, например использование пула ресурсов, при котором для получения ресурса потенциально необходимо дождаться завершения некоторых работ по очистке старых ресурсов. Текущий Futures API может использоваться и использовался для эффективного решения этих проблем в производственных системах.
Однако эта проблема касается стабилизации async / await, которая ортогональна дизайну Futures API, который уже стабилизировался. Не стесняйтесь задавать дополнительные вопросы или открывать вопрос для обсуждения по репо futures-rs.
@Ekleog
Можно ли явно добавить «Проблемы Triage AsyncAwait-Unclear» к блокираторам стабилизации (и / или зарегистрировать беспокойство по этому поводу)?
Ага, мы этим занимаемся каждую неделю. Запишите эту конкретную проблему (# 60414), я считаю ее важной и хотел бы, чтобы она была исправлена, но мы еще не смогли решить, должна ли она блокировать стабилизацию, тем более что это уже наблюдается в -> impl Trait
functions.
@cramertj Спасибо! Я думаю, что проблема # 60414 в основном заключается в том, что «ошибка может возникнуть очень быстро сейчас», в то время как с -> impl Trait
похоже, что никто даже не заметил ее раньше - тогда ничего страшного, если она все равно будет отложена, некоторые проблемы придется :) (FWIW это возникло в естественном коде в функции, где я возвращаю как ()
в одном месте, и T::Assoc
в другом месте, из-за чего IIRC не смог заставить его скомпилировать - не проверял код с момента открытия # 60414, так что, возможно, я ошибаюсь)
@Ekleog Да, в этом есть смысл! Я определенно понимаю, почему это было бы больно - я создал поток zulip, чтобы больше погрузиться в эту конкретную проблему.
РЕДАКТИРОВАТЬ: неважно, я пропустил цель 1.38
.
@cramertj
Для этого есть несколько различных способов, например использование пула ресурсов, при котором для получения ресурса потенциально необходимо дождаться завершения некоторых работ по очистке старых ресурсов.
Разве они не менее эффективны по сравнению с хранением буферов как части будущего состояния? Меня больше всего беспокоит то, что текущий дизайн не будет иметь нулевых затрат (в том смысле, что вы сможете создать более эффективный код, отказавшись async
абстракции
@герцог
Команда lang, конечно, может судить об этом лучше меня, но откладывание до
1.38
для обеспечения стабильной реализации казалось бы гораздо более разумным.
Эта проблема нацелена на 1.38, см. Первую строку описания.
@huxi спасибо, я это пропустил. Отредактировал свой комментарий.
@newpavlov
Разве они не менее эффективны по сравнению с хранением буферов как части будущего состояния? Меня больше всего беспокоит то, что текущий дизайн не будет иметь нулевую стоимость (в том смысле, что вы сможете создать более эффективный код, отказавшись от асинхронной абстракции) и менее эргономичным для API на основе завершения, и нет четкого способа исправить Это. Это ни в коем случае не шумиха, но я думаю, что важно не забывать о подобных недостатках в дизайне, поэтому просьба упомянуть об этом в OP.
Нет, не обязательно, но давайте перенесем это обсуждение в отдельную тему, поскольку она не связана со стабилизацией async / await.
(Тем не менее, я хотел бы видеть упоминание о проблемах с мостом к API-интерфейсам системы на основе завершения и асинхронной отмены в будущих возможностях. TL; DR им по-прежнему приходится передавать собственные буферы. Это проблема библиотеки, но одна с упоминанием.)
Я также хотел бы увидеть упоминание о проблемах с API на основе завершения. (см. этот внутренний поток для контекста). Учитывая IOCP и введение io_uring, которое может стать путем для асинхронного ввода-вывода в Linux, я думаю, что важно иметь четкий путь для их обработки.
Я согласен с Тейлором в том, что обсуждение проектов API в этом проблемном пространстве было бы не по теме, но я хочу затронуть один конкретный аспект этих комментариев (и это обсуждение io_uring в целом), который имеет отношение к стабилизации async / await: проблема сроки.
io_uring - это интерфейс, который появится в Linux в этом , 2019 году. Проект Rust работает над абстракцией фьючерсов с 2015 года, четыре года назад. Фундаментальный выбор в пользу опроса, основанного на API, основанного на завершении, произошел в 2015 и 2016 годах. На RustCamp в 2015 году Карл Лерш рассказал о том, почему он сделал этот выбор в mio, базовой абстракции ввода-вывода. В этом сообщении в блоге в 2016 году Аарон Турон рассказал о преимуществах создания абстракций более высокого уровня. Эти решения были приняты очень давно, и без них мы не смогли бы дойти до того, что мы сейчас находимся.
Предложения о том, что нам следует пересмотреть нашу базовую модель будущего, - это предложения о том, что нам следует вернуться к состоянию, в котором мы были 3 или 4 года назад, и начать с этого момента. Какого рода абстракция могла бы охватывать модель ввода-вывода на основе завершения без введения накладных расходов для примитивов более высокого уровня, как описал Аарон? Как мы сопоставим эту модель с синтаксисом, который позволяет пользователям писать «обычные аннотации Rust + второстепенные», как это делает async / await? Как мы сможем справиться с интеграцией этого в нашу модель памяти, как мы это сделали для этих конечных автоматов с помощью вывода? Попытка дать ответы на эти вопросы была бы не по теме этой темы; Дело в том, что ответить на них и доказать правильность ответов - это работа. То, что до сих пор составляет солидное десятилетие трудовых лет между разными участниками, придется переделывать снова.
Цель Rust - выпустить продукт, который люди смогут использовать, а это значит, что мы должны его выпустить . Мы не всегда можем перестать смотреть в будущее на то, что может стать большим событием в следующем году, и перезапускать наш процесс проектирования, чтобы учесть это. Мы делаем все, что в наших силах, исходя из ситуации, в которой мы оказались. Очевидно, может быть неприятно чувствовать, что мы едва пропустили что-то важное, но в настоящее время у нас нет полного представления: а) о том, какой лучший результат для обработки io_uring, б) насколько важным будет io_uring для экосистемы в целом. На основании этого мы не можем отменить 4 года работы.
Подобные, возможно, даже более серьезные ограничения Rust уже существуют в других областях. Хочу выделить одну, на которую я смотрел с Ником Фицджеральдом прошлой осенью: интеграция с wasm GC. План обработки управляемых объектов в wasm состоит в том, чтобы по существу сегментировать пространство памяти, чтобы они существовали в отдельном адресном пространстве от неуправляемых объектов (действительно, когда-нибудь во многих отдельных адресных пространствах). Модель памяти Rust просто не предназначена для обработки отдельных адресных пространств, и любой небезопасный код, который сегодня имеет дело с кучей памяти, предполагает, что существует только одно адресное пространство. Хотя мы набросали как критические, так и технически неразрывные, но чрезвычайно разрушительные технические решения, наиболее вероятный путь вперед - это признать, что наша история с wasm GC может быть не совсем оптимальной , потому что мы имеем дело с ограничениями Rust как это существует.
Интересный аспект, который мы здесь стабилизируем, заключается в том, что мы делаем самодостаточные структуры доступными из безопасного кода. Что делает это интересным, так это то, что в Pin<&mut SelfReferentialGenerator>
у нас есть изменяемая ссылка (хранящаяся как поле в Pin
), указывающая на все состояние генератора, и у нас есть указатель внутри этого состояния, указывающий в другой кусок государства. Этот внутренний указатель является псевдонимом изменяемой ссылки!
Насколько мне известно, изменяемая ссылка не используется для фактического доступа к той части памяти, на которую указывает указатель на другое поле. (В частности, не существует метода clone
или такого, который мог бы читать поле указателя с использованием любого другого указателя, кроме самореферентного.) Тем не менее, это становится все ближе к наличию изменяемого псевдонима ссылки с нечто большее, чем что-либо еще в основной экосистеме, в частности все, что поставляется с самим rustc. «Линия», по которой мы здесь движемся, становится очень тонкой, и мы должны быть осторожны, чтобы не потерять все эти прекрасные оптимизации, которые мы хотим сделать на основе изменяемых ссылок.
Вероятно, на данный момент мы мало что можем с этим поделать, в частности, потому что Pin
уже стабилен, но я считаю, что стоит отметить, что это значительно усложнит все правила, для которых разрешено использование псевдонимов и который не. Если вы думали, что Stacked Borrows - это сложно, приготовьтесь к тому, что ситуация ухудшится.
Копия https://github.com/rust-lang/unsafe-code-guidelines/issues/148
Насколько мне известно, изменяемая ссылка не используется для фактического доступа к той части памяти, на которую указывает указатель на другое поле.
Люди говорили о том, чтобы все эти типы сопрограмм реализовывали Debug
, похоже, что этот разговор также должен включать рекомендации по небезопасному коду, чтобы быть уверенным, что безопасно отлаживать печать.
Люди говорили о том, чтобы все эти типы сопрограмм реализовывали отладку, похоже, что этот разговор также должен включать рекомендации по небезопасному коду, чтобы быть уверенным, что безопасно отлаживать печать.
Действительно. Такая реализация Debug
, если она печатает поля с самими ссылками, скорее всего, запретит оптимизацию на основе ссылок на уровне MIR внутри генераторов.
Обновление по блокираторам:
Оба блокиратора высокого уровня достигли большого прогресса и, возможно, оба уже закончили (?). Было бы здорово получить дополнительную информацию от @cramertj @tmandry и @nikomatsakis :
Это оставляет документацию и тестирование как основные препятствия для стабилизации этой функции. @Centril неоднократно выражал озабоченность по поводу того, что эта функция недостаточно хорошо протестирована или отполирована; @Centril есть ли где-нибудь, где вы перечислили конкретные проблемы, которые можно было бы
Не уверен, водит ли кто документацию. Любой, кто хочет сосредоточиться на улучшении древовидной документации в книге, справочнике и т. Д., Окажет большую услугу! Документация вне дерева, например, в репозитории Futures или areweasyncyet, имеет немного дополнительного времени.
На сегодняшний день у нас есть 6 недель до закрытия бета-версии, поэтому предположим, что у нас есть 4 недели (до 1 августа), чтобы сделать все это, чтобы быть уверенными, что мы не упустим 1.38.
Вопрос размера более неоднозначен; всегда будет больше оптимизаций, но я думаю, что низко висящий плод отказа от очевидного экспоненциального увеличения ножных ружей в основном решен?
Думаю, да, и некоторые другие недавно закрылись; но есть и другие проблемы с блокировкой .
@Centril есть ли где-нибудь, где вы перечислили конкретные проблемы, которые можно было бы
Там есть папка со списком вещей, которые мы хотели протестировать, и https://github.com/rust-lang/rust/issues/62121. Помимо этого, я постараюсь как можно скорее пересмотреть области, которые, по моему мнению, недостаточно протестированы. Тем не менее, некоторые области сейчас довольно хорошо протестированы.
Любой, кто хочет сосредоточиться на улучшении древовидной документации в книге, справочнике и т. Д., Окажет большую услугу!
Действительно; Буду рад пересмотреть PR к ссылке. Также cc @ehuss.
Я также хотел бы переместить async unsafe fn
из MVP в его собственные ворота функций, потому что я думаю: а) он мало используется, б) он не особенно хорошо протестирован, в) он якобы ведет себя странно, потому что .await
point - это не то место, где вы пишете unsafe { ... }
и это понятно из "дырявой реализации POV", но не столько из POV эффектов, d) он мало обсуждался и не был включен в RFC ни этот отчет, и д) мы сделали это с помощью const fn
и он работал нормально. (Я могу написать PR функции ворот)
Я нормально отношусь к дестабилизации async unsafe fn
, хотя я скептически отношусь к тому, что мы закончим с другим дизайном, чем нынешний. Но кажется разумным дать нам время разобраться в этом!
Я создал https://github.com/rust-lang/rust/issues/62500 для перемещения async unsafe fn
в отдельный элемент доступа и внес его в список блокировщиков. Думаю, нам, вероятно, также следует создать проблему с правильным отслеживанием.
Я очень скептически отношусь к тому, что мы достигнем другого дизайна для async unsafe fn
и удивлен решением не включать его в начальный раунд стабилизации. Я написал несколько async fn
s, которые небезопасны и, я полагаю, сделают их async fn really_this_function_is_unsafe()
или что-то в этом роде. Это похоже на регресс базовых ожиданий пользователей Rust в плане возможности определять функции, для вызова которых требуется unsafe { ... }
. Еще одно окно функций будет способствовать созданию впечатления, что async
/ await
незавершено.
@cramertj, кажется, мы должны обсудить! Я создал для него тему Zulip , чтобы не допустить чрезмерной перегрузки этой проблемы с отслеживанием.
Что касается будущих размеров, оптимизированы случаи, влияющие на каждую точку await
. Последняя оставшаяся проблема, о которой я знаю, - это # 59087, где любое заимствование будущего до ожидания может удвоить размер, выделенный для этого будущего. Это довольно прискорбно, но все же немного лучше, чем мы были раньше.
У меня есть представление о том, как исправить эту проблему, но если это не встречается чаще, чем я представляю, вероятно, это не должно быть препятствием для стабильного MVP.
Тем не менее, мне все еще нужно посмотреть на влияние этих оптимизаций на Fuchsia (это было заблокировано некоторое время, но должно исчезнуть сегодня или завтра). Вполне возможно, что мы обнаружим больше случаев, и нам нужно будет решить, следует ли какой-либо из них блокировать.
@cramertj (Напоминание: я использую async / await и хочу, чтобы он стабилизировался как можно скорее) Ваш аргумент звучит как аргумент в пользу задержки стабилизации async / await, а не для стабилизации async unsafe
прямо сейчас без надлежащих экспериментов и размышлений.
Тем более, что он не был включен в RFC, и потенциально может вызвать еще один дерьмовый шторм «имплицитный признак в позиции аргумента», если он будет вытеснен таким образом.
[Боковое примечание, которое не заслуживает здесь обсуждения: «Еще одна функция создает впечатление, что async / await не завершена», я обнаруживал ошибку каждые несколько часов использования async / await, распространяемую немногими месяцы, законно необходимые команде rustc для их исправления, и это то, что заставляет меня говорить, что это незавершенное. Последняя проблема была исправлена несколько дней назад, и я очень надеюсь, что не обнаружу еще одну, когда снова попытаюсь скомпилировать свой код с помощью более новой версии rustc, но ...]
Ваш аргумент звучит как аргумент в пользу отсрочки стабилизации async / await, а не для стабилизации небезопасного асинхронного режима прямо сейчас без надлежащих экспериментов и размышлений.
Нет, это не аргумент в пользу этого. Я считаю, что async unsafe
готов, и не могу представить для него другой дизайн. Я считаю, что если его не включить в этот первоначальный выпуск, это приведет только к негативным последствиям. Я не верю, что отсрочка async
/ await
в целом или async unsafe
частности не даст лучшего результата.
не могу представить для этого другого дизайна
Альтернативный дизайн, хотя и тот, который определенно требует сложных расширений: async unsafe fn
- это unsafe
для .await
, а не call()
. Причина этого в том, что _не может быть сделано ничего опасного_ в точке, где вызывается async fn
и создает impl Future
. Все, что делает этот шаг, - это помещает данные в структуру (фактически, все async fn
- это const
для вызова). Фактическая точка опасности - продвигаться в будущее с помощью poll
.
(imho, если unsafe
происходит немедленно, unsafe async fn
имеет больше смысла, а если unsafe
задерживается, async unsafe fn
имеет больше смысла.)
Конечно, если мы никогда не сможем сказать, например, unsafe Future
где все методы Future
небезопасны для вызова, тогда «поднятие» unsafe
на создание impl Future
, и контракт этого unsafe
заключается в безопасном использовании полученного будущего. Но это также можно почти тривиально сделать без unsafe async fn
, просто «обессахаривая» вручную в блок async
: unsafe fn os_stuff() -> impl Future { async { .. } }
.
Вдобавок к этому, однако, возникает вопрос, существует ли на самом деле способ иметь инварианты, которые необходимо сохранять после запуска poll
ing, которые не нужно сохранять при создании. В Rust часто используется конструктор unsafe
для безопасного типа (например, Vec::from_raw_parts
). Но главное здесь то, что после построения тип _ нельзя_ неправильно использовать; область действия unsafe
завершена. Такая оценка небезопасности является ключом к гарантиям Rust. Если вы введете unsafe async fn
который упаковывает безопасный impl Future
с требованиями к тому, как и когда он опрашивается, а затем передаете его в безопасный код, этот безопасный код внезапно оказывается внутри вашего небезопасного барьера. И это _ очень_ вероятно произойдет, как только вы воспользуетесь этим будущим любым способом, кроме немедленного его ожидания, поскольку оно, скорее всего, будет проходить через _ некоторый_ внешний комбинатор.
Я предполагаю, что TL; DR этого заключается в том, что определенно есть углы async unsafe fn
которые следует обсудить должным образом, прежде чем стабилизировать его, особенно с потенциальным введением направления const Trait
(у меня есть черновик блога сообщение об обобщении этого на "систему слабых" эффектов "с любым ключевым словом fn
). Тем не менее, unsafe async fn
может быть достаточно ясным относительно «порядка» / «позиционирования» unsafe
для стабилизации.
Я считаю, что основанная на эффектах черта unsafe Future
не только недоступна для всего, что мы знаем, как выразить на языке или компиляторе сегодня, но что в конечном итоге это будет худший дизайн из-за дополнительного эффекта - полиморфизм, который потребуются комбинаторам.
Ничего небезопасного сделать нельзя в точке, где вызывается async fn и создает имплицитное будущее. Все, что делает этот шаг, - это помещает данные в структуру (фактически, все async fn вызываются как const). Фактическая точка небезопасности - опросы в будущее.
Это правда, что, поскольку async fn
не может запускать какой-либо пользовательский код до .await
ed, любое неопределенное поведение, вероятно, будет отложено до вызова .await
. Я думаю, однако, что есть важное различие между точкой UB и точкой unsafe
ty. Фактическая точка unsafe
ty - это то место, где автор API решает, что пользователь должен пообещать, что выполняется набор нестатически проверяемых инвариантов, даже если результат нарушения этих инвариантов не приведет к UB пока позже в другом безопасном коде. Одним из распространенных примеров этого является функция unsafe
для создания значения, реализующего признак с помощью безопасных методов (именно то, что это такое). Я видел, как это использовалось, чтобы гарантировать, что, например, типы, реализующие Visitor
-trait, реализации которых полагаются на инварианты unsafe
могут быть надежно использованы, требуя unsafe
для создания типа. Другие примеры включают такие вещи, как slice::from_raw_parts
, которые сами по себе не вызывают UB (не считая инвариантов валидности типа), но доступ к результирующему срезу будет.
Я не верю, что async unsafe fn
представляет собой уникальный или интересный случай здесь - он следует хорошо зарекомендовавшему себя шаблону для выполнения поведения unsafe
за безопасным интерфейсом, требуя unsafe
конструктор.
@cramertj Тот факт, что вам даже
Напомним, цитата из ридми:
Вам необходимо выполнить этот процесс, если [...]:
- Любое семантическое или синтаксическое изменение языка, не являющееся исправлением ошибки.
- [... а также нецитированные материалы]
Я не говорю, что в текущий дизайн произойдут какие-либо изменения. На самом деле, подумав об этом несколько минут, я подумал, что это, вероятно, лучший дизайн, который я мог придумать. Но процесс - это то, что позволяет нам избежать того, чтобы наши убеждения стали опасными для Rust, и нам не хватает мудрости многих людей, которые следят за репозиторием RFC, но не читают каждую проблему, не следуя процессу здесь.
Иногда имеет смысл не следовать процессу. Здесь я не вижу срочности, которая могла бы служить основанием для игнорирования процесса только во избежание двухнедельной задержки FCP.
Поэтому, пожалуйста, позвольте rust быть честным со своим сообществом в отношении обещаний, которые он дает в собственном файле readme, и просто держите эту функцию ниже ворот функций, пока не будет хотя бы принятый RFC и, надеюсь, еще какое-то его использование в дикой природе. Будь то шлюз функции async / await целиком или просто шлюз небезопасно-асинхронной функции, мне все равно, но просто не стабилизируйте то, что (AFAIK) мало использовалось за пределами async-wg и почти не известно в общее сообщество.
Я пишу первый проход по справочному материалу для книги. Попутно я заметил, что в RFC async-await сказано, что поведение оператора ?
еще не определено. И все же, похоже, он отлично работает в асинхронном блоке ( игровая площадка ). Должны ли мы переместить это в отдельное окно функций? Или это было решено в какой-то момент? Я не видел этого в отчете о стабилизации, но, возможно, пропустил.
(Я также задавал этот вопрос в Zulip и предпочел бы ответы там, так как мне легче управлять.)
Да, это обсуждалось и решалось вместе с поведением return
, break
, continue
et. al. которые все делают «единственно возможное» и ведут себя так, как если бы они были внутри замыкания.
let f = unsafe { || {...} };
также безопасно вызывать, а IIRC это эквивалентно перемещению unsafe
внутрь закрытия.
То же самое для unsafe fn foo() -> impl Fn() { || {...} }
.
Для меня это достаточно прецедент, когда «небезопасная вещь происходит после выхода из области unsafe
».
То же самое и в других местах. Как указывалось ранее, unsafe
не всегда находится там, где мог бы быть потенциальный UB. Пример:
let mut vec: Vec<u32> = Vec::new();
unsafe { vec.set_len(100); } // <- unsafe
let val = vec.get(5).unwrap(); // <- UB
println!("{}", val);
Мне это просто кажется неправильным пониманием небезопасности - unsafe не означает, что «здесь происходит небезопасная операция» - это означает «Я гарантирую, что соблюдаю необходимые инварианты здесь». Хотя вы можете поддерживать инварианты в точке ожидания, поскольку он не включает в себя переменных параметров, это не очень очевидный сайт для проверки того, что вы поддерживаете инварианты. Это имеет гораздо больше смысла и гораздо больше соответствует тому, как работают все наши небезопасные абстракции, чтобы гарантировать соблюдение инвариантов на сайте вызова.
Это связано с тем, почему представление о небезопасности как об эффекте приводит к неточным интуициям (как утверждал Ральф, когда эта идея впервые была высказана в прошлом году). Небезопасность специально, преднамеренно, не заразна. Хотя вы можете писать небезопасные функции, которые вызывают другие небезопасные функции и просто перенаправляют их инварианты вверх по стеку вызовов, это не обычный способ использования unsafe, и на самом деле это синтаксический маркер, используемый для определения контрактов на значения и ручной проверки этого вы их поддерживаете.
Так что дело не в том, что для каждого проектного решения нужен целый RFC, но мы работали над тем, чтобы обеспечить большую ясность и структуру того, как принимаются решения. Список основных моментов для принятия решения в открытии этого выпуска является тому примером. Используя доступные нам инструменты, я хотел бы попытаться достичь структурированного консенсуса по этой проблеме небезопасных асинхронных fns, так что это сводный пост с опросом.
async unsafe fn
async unsafe fns - это асинхронные функции, которые можно вызывать только внутри небезопасного блока. Внутри их тело считается небезопасным прицелом. Основной альтернативой было бы сделать async unsafe fns небезопасным для ожидания , а не для вызова. Есть ряд веских причин предпочесть тот дизайн, в котором называть их небезопасно:
@rfcbot ask lang "
Понятия не имею, как провести опрос с помощью rfcbot, но, по крайней мере, я его номинировал.
Член команды @withoutboats обратился к командам: T-lang за консенсусом по:
«Принимаем ли мы стабилизацию async unsafe fn как async fn, вызывать которую небезопасно?»
@withoutboats
Я хотел бы нанести удар по структурированному консенсусу по этой проблеме небезопасных async fns, так что это сводный пост с опросом.
Спасибо, что написали. Обсуждение убедило меня, что async unsafe fn
как он работает сегодня ночью, ведет себя правильно. (Вероятно, следует добавить некоторые тесты, поскольку он выглядел скудным.) Кроме того, не могли бы вы внести в отчет вверху части отчета + описание того, как ведет себя async unsafe fn
?
Это больше соответствует тому, как небезопасно работает в целом. Небезопасная функция - это абстракция, которая зависит от некоторых инвариантов, поддерживаемых ее вызывающей стороной. То есть дело не в том, что речь идет о маркировке «где происходит небезопасная операция», а в том, «где инвариант гарантированно соблюдается». Гораздо разумнее проверять, поддерживаются ли инварианты на сайте вызова, где аргументы фактически указаны, чем на сайте ожидания, отдельно от того, когда аргументы были выбраны и проверены. Это очень нормально для небезопасных функций в целом, которые часто определяют какое-то состояние, которое другие безопасные функции ожидают быть правильными.
Как человек, не обращающий слишком пристального внимания, я согласен и думаю, что решение здесь - хорошая документация.
Я мог бы ошибиться здесь, но с учетом этого
Мне кажется, что инварианты, зависящие от конкретного ожидающего использования / поведения, находятся где-то между плохой идеей и невозможностью безопасного правила.
Если есть случаи, когда ожидаемое выходное значение - это то, что участвует в поддержании инвариантов, я предполагаю, что в будущем может просто быть выход, который является оболочкой, требующей небезопасного доступа, например
struct UnsafeOutput<T>(T);
impl<T> UnsafeOutput<T> {
unsafe fn unwrap(self) -> T { self.0 }
}
Учитывая, что значение unsafe
предшествует значению async
в этом "раннем небезопасном состоянии", я был бы гораздо более счастлив, если бы порядок модификаторов был unsafe async fn
чем async unsafe fn
, потому что unsafe (async fn)
гораздо более явно отображает это поведение, чем async (unsafe fn)
.
Я с радостью соглашусь, но я твердо уверен, что в представленном здесь порядке упаковки есть unsafe
снаружи, и порядок модификаторов может помочь прояснить это. ( unsafe
- модификатор async fn
, а не async
модификатор unsafe fn
.)
Я с радостью приму и то, и другое, но я твердо уверен, что в представленном здесь порядке упаковки есть
unsafe
снаружи, и порядок модификаторов может помочь прояснить это. (unsafe
- модификаторasync fn
, а неasync
модификаторunsafe fn
.)
Я был с вами до вашего последнего пункта в скобках. Запись @withoutboats дает мне довольно ясно понять, что, если небезопасность рассматривается на сайте вызова, на самом деле у вас есть unsafe fn
(который вызывается в асинхронном контексте).
Я бы сказал, мы покрасим велосипедную навесу async unsafe fn
.
Я думаю, что async unsafe fn
имеет больше смысла, но я также считаю, что мы должны грамматически принимать любой порядок между async, unsafe и const. Но для меня async unsafe fn
имеет больше смысла, поскольку вы удаляете асинхронный код и изменяете возвращаемый тип, чтобы "десахарировать" его.
Альтернативу невозможно реализовать в ближайшей или среднесрочной перспективе (имеется в виду несколько лет). Невозможно создать будущее, которое было бы небезопасно опрашивать на языке Rust, разработанном в настоящее время.
FWIW Я столкнулся с аналогичной проблемой, о которой упоминал в RFC2585, когда дело доходит до замыканий внутри unsafe fn
и свойств функции. Я не ожидал, что unsafe async fn
вернет Future
с безопасным методом poll
, а вместо этого вернет UnsafeFuture
с unsafe
метод опроса. (*) Тогда мы могли бы заставить .await
также работать с UnsafeFuture
s, когда он используется внутри блоков unsafe { }
, но не иначе.
Эти две будущие черты будут огромным изменением по сравнению с тем, что у нас есть сегодня, и они, вероятно, внесут много проблем с возможностью компоновки. Значит, корабль для исследования альтернатив, вероятно, ушел. В частности, поскольку это будет отличаться от того, как сегодня работают черты Fn
(например, у нас нет черты UnsafeFn
или аналогичной, и моя проблема в RFC2585 заключалась в том, что создание замыкания внутри unsafe fn
возвращает закрытие, которое impls Fn()
, то есть безопасное для вызова, даже если это закрытие может вызывать небезопасные функции.
Создание «небезопасного» будущего или закрытие не является проблемой, проблема состоит в том, чтобы вызвать их, не доказывая, что это безопасно, особенно когда их типы не говорят, что это должно быть сделано.
(*) Мы можем предоставить общий набор UnsafeFuture
для всех Future
s, а также можем предоставить UnsafeFuture
unsafe
метод Future
который безопасен для poll
.
Вот мои два цента:
unsafe
async-функции - это правильный дизайн.unsafe
и async
unsafe async fn
потому что порядок кажется более логичным. Подобно «быстрому электромобилю» против «быстрому электрическому автомобилю». В основном потому, что async fn
обессахаривает fn
. Итак, логично, что два ключевых слова находятся рядом друг с другом.Я думаю, что let f = unsafe { || { ... } }
должно сделать f
безопасным, признак UnsafeFn
никогда не должен вводиться, и априори .await
ing и async unsafe fn
должны быть безопасно. Любой UnsafeFuture
требует веского обоснования!
Все это следует потому, что unsafe
должно быть явным, а Rust должен подтолкнуть вас обратно в безопасную страну. Также по этому токену f
...
должно _не_ быть небезопасным блоком, следует принять https://github.com/rust-lang/rfcs/pull/2585 и async unsafe fn
должно быть безопасное тело.
Я думаю, что последний пункт может оказаться весьма важным. Возможно, что в каждом async unsafe fn
будет использоваться блок unsafe
, но аналогично большинству из них будет полезен некоторый анализ безопасности, и многие из них звучат достаточно сложно, чтобы легко допустить ошибку.
Мы никогда не должны обходить проверку заимствований при захвате, в частности, для закрытия.
Итак, мой комментарий здесь: https://github.com/rust-lang/rust/issues/62149#issuecomment -511116357 - очень плохая идея.
Признак UnsafeFuture
потребует от вызывающего написать unsafe { }
для опроса будущего, но вызывающий не знает, какие обязательства должны быть там подтверждены, например, если вы получите Box<dyn UnsafeFuture>
unsafe { future.poll() }
безопасно? Для всех фьючерсов? Вы не можете знать. Так что это было бы совершенно бесполезно, как указал @rpjohnst в разногласиях для аналогичной черты UnsafeFn
.
Требовать, чтобы будущее всегда было безопасным для опроса, имеет смысл, и процесс построения будущего, которое должно быть безопасным для опроса, может быть небезопасным; Полагаю, вот что такое async unsafe fn
. Но в этом случае элемент fn
может задокументировать, что необходимо поддержать, чтобы возвращенное будущее можно было безопасно опросить.
@rfcbot реализация-работа-блокировка-стабилизация
Насколько мне известно, есть еще 2 известных блокиратора реализации (https://github.com/rust-lang/rust/issues/61949, https://github.com/rust-lang/rust/issues/62517), и это будет еще будет хорошо добавить несколько тестов. Я разрешаю свою заботу о том, чтобы rfcbot не был нашим блокировщиком по времени, и тогда мы фактически заблокируем исправления.
@rfcbot разрешить реализацию-работу-блокировку-стабилизацию
: bell: Сейчас наступает последний период для комментариев , согласно обзору выше . : звонок:
Зарегистрирован PR стабилизации в https://github.com/rust-lang/rust/pull/63209.
Последний период комментариев с предложением о слиянии в соответствии с приведенным выше обзором завершен .
Как автоматизированный представитель процесса управления, я хотел бы поблагодарить автора за их работу и всех, кто внес свой вклад.
RFC скоро будет объединен.
Интересный аспект, который мы здесь стабилизируем, заключается в том, что мы делаем самодостаточные структуры доступными из безопасного кода. Что делает это интересным, так это то, что в Pin <& mut SelfReferentialGenerator> у нас есть изменяемая ссылка (сохраненная как поле в Pin), указывающая на все состояние генератора, и у нас есть указатель внутри этого состояния, указывающий на другую часть состояния. . Этот внутренний указатель является псевдонимом изменяемой ссылки!
Как продолжение этого, @comex действительно удалось написать некоторый (безопасный) асинхронный код на Rust, который нарушает аннотации LLVM noalias
том виде, в
Самый полезный комментарий
Последний период комментариев с предложением о слиянии в соответствии с приведенным выше обзором завершен .
Как автоматизированный представитель процесса управления, я хотел бы поблагодарить автора за их работу и всех, кто внес свой вклад.
RFC скоро будет объединен.