Rust: Проблема отслеживания для RFC 2046, значение разрыва метки

Созданный на 27 февр. 2018  ·  135Комментарии  ·  Источник: rust-lang/rust

Это проблема отслеживания для RFC 2046 (rust-lang / rfcs # 2046).

Шаги:

Нерешенные вопросы:

B-RFC-implemented B-unstable C-tracking-issue F-label_break_value T-lang

Самый полезный комментарий

@withoutboats

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

Я согласен с этим принципом и думаю, что эта функция соответствует этой полосе (знакомый поток управления, ориентированный на конкретные, важные варианты использования). Было несколько раз, когда я просматривал или писал код, подобный приведенным выше примерам, когда я чувствовал, что это, безусловно, самый чистый и легкий для чтения способ написания этого кода. Повторное использование существующих конструкций потока управления, таких как loop с безусловным break в конце, вводит пользователя в заблуждение, а немедленно применяемые функции часто недостаточны из-за использования ? , await! или другие промежуточные конструкции потока управления.

Использование loop + завершающего break сбивает с толку, и вместо этого мы должны предпочесть, чтобы пользователи заявляли о своем реальном намерении, а не требовали от них злоупотребления инструментом, который был создан для другого стиля потока управления. Это похоже на то, что у нас даже есть конструкция loop , в то время как другие языки довольствуются while true { ... } . Это позволяет писать код, который выражает более четкое намерение и в результате становится более читабельным.

Вдобавок я всегда ожидал , что эта функция

TL; DR: я думаю, что эта функция поддерживает реальные варианты использования, которые могут быть записаны иначе только с помощью сильно вложенных операторов if или путем злоупотребления другими конструкциями, такими как loop-break-value. Это небольшое дополнение к поверхностному синтаксису, которое заставляет блоки вести себя так, как я ожидал, и позволяет мне писать код, который я имею в виду, а не что-то более хакерское.

Все 135 Комментарий

Использование "return" могло бы иметь интересные последствия для помеченного ? (оператор вопросительного знака tryop).

Использовать return в качестве ключевого слова вместо break?

@ mark-im и @joshtriplett уже высказались против возврата, но я присоединюсь, учитывая, что это, по-видимому, все еще нерешенный вопрос.

break (и continue) можно использовать только с циклом.
Если ты можешь что-то сломать, можешь продолжать. (Я не думаю, что есть очевидный синтаксис для продолжения блока.)

В C, C ++, Java, C #, JavaScript и , вероятно , больше языков вы обычно break ING переключающего заявления для того , чтобы предотвратить падение сквозным. Rust решил эту задачу намного лучше с помощью | в шаблонах, но люди, пришедшие с этих языков, на самом деле не увидят break как что-то только для циклов. Тем более, что Java и JavaScript также предоставляют эту функцию через break а не через return .

Однако аргумент о «правилах, которые следует помнить» действительно хорошо работает в другом направлении. Насколько я могу судить, это общая черта упомянутых языков, а также Rust, что return применяется только к функциям и ни к чему другому. Итак, если вы видите возврат, вы знаете, что функция остается.

Пометка блока не вызывает ошибок на существующих немаркированных разрывах

Во-первых, я думаю, что это случается очень редко, поскольку помеченная функция разрыва, по общему признанию, не является чем-то, что будет использоваться 10 раз на 1000 строк. В конце концов, это будет применяться только к немаркированным разрывам, которые пересекают границу блока, а не к немаркированным разрывам внутри блока. Во-вторых, пользователи Rust привыкли к жалобам / сообщениям об ошибках со стороны компилятора, они с радостью их исправят! В-третьих (я думаю, это самый сильный момент), если вместо того, чтобы маркировать блок, вы оборачиваете его в цикл, вам уже нужно следить за немаркированными разрывами, и нет сообщения об ошибке, которое удобно перечисляет операторы break, у вас есть самому охотиться на них :).

Тем более, что Java и JavaScript также предоставляют эту функцию через break, а не return.

Для меня это главное. разрыв с блоками есть во многих языках. возврат из блоков ... не так много.

Лично я разделяю точку зрения break вместо return , но мне показалось, что обсуждение RFC не было решено ... Если вы верите в вопрос решается в команде lang, поставьте галочку в поле с пометкой =)

Просто говорю, что работаю над этим. Не нужны инструкции наставника. Просто чтобы не дублировать усилия. Скоро ждите пиара.

Я по-прежнему за return вместо break , но могу согласиться с этим не согласиться. Вопрос решен.

В настоящее время (с rustc 1.28.0-nightly (a1d4a9503 2018-05-20) ) rustc не разрешает unsafe на помеченном блоке. Ожидается ли это?

@topecongiro Да, я считаю, что это намеренно, что в настоящее время это разрешено только для простых блоков. Это может измениться в будущем, но, учитывая, что это такая низкоуровневая и необычная функция, я склоняюсь к тому, чтобы это была функция, а не ограничение. (В крайнем случае, я точно не хочу else 'a: { .)

Однозначно согласен. Небезопасный + необычный поток управления звучит как нечто обескураживающее.

Однако в крайнем случае вы можете использовать:

'a: {
unsafe {...}
}

Правильно?

На самом деле, altho else создает новую лексическую область видимости, а не блок. Все if-else - это блок (вроде как). Так что нет, вы не получите else 'a: { вы получите 'a: if ... else { .

else содержит блок (выражение). без блоков не бывает «новой лексической области».
Еще худшая позиция синтаксиса поверхности, чем else , будет 'a: while foo 'b: {...} .
(что интересно, continue 'a - это break 'b , мы могли бы полагаться на это, по крайней мере, внутри)

(что интересно, continue 'a - это break 'b , мы могли бы полагаться на это, по крайней мере, внутри)

Замечательное наблюдение!

Я думаю, что метки должны быть частью выражений, содержащих блоки, а не сами блоки. У нас уже есть прецедент с loop . (Как это часто бывает, простой блок сам по себе также является выражением, содержащим блок. Но такие вещи, как if и loop являются выражениями, содержащими блоки, и я полагаю, что они не являются блоками.)

(Такие вещи, как while или for не должны поддерживать значение label-break-value, потому что они могут или не могут возвращать значение в зависимости от того, завершаются ли они нормально или с помощью break .)

@eddyb

(что интересно, continue 'a is break' b, мы могли бы полагаться на это, по крайней мере, внутренне)

Только если break 'b перепроверит условие цикла ...

@ mark-im Это эквивалентно 'a: while foo {'b: {...}} , break не будет проверять условие цикла, сам цикл будет, потому что условие цикла проверяется перед каждой итерацией основного блока.

Ого, я нахожу это очень не интуитивным. Я ожидаю, что break 'b будет в основном goto 'b , что означает, что мы никогда не выходим из тела цикла, и условие больше не проверяется ...

О: man_facepalming: Понятно ...

Вот почему мне не нравятся надписи break / continue: /

Что ж, у нас конкретно нет возможности маркировать эти странные внутренние блоки, поэтому я не вижу проблемы. break всегда означает «покинуть этот блок», и, учитывая указанное выше ограничение, это не может означать ничего, кроме «перейти к месту после связанной закрывающей скобки».

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

Хорошо, я понимаю, что доступность - большая проблема для языков программирования ... однако пометка break чрезвычайно полезна, если вы пишете код, как я.

Итак, как мы можем сделать помеченный перерыв более доступным?

Итак, как мы можем сделать помеченный перерыв более доступным?

Это отличный вопрос. Некоторые идеи у меня были:

  • Мы должны собрать несколько примеров того, как люди используют это в дикой природе. Мы можем искать нежелательные закономерности или ленивые привычки.
  • Нам следовало бы придерживаться самоуверенного стиля, когда использование пометок break / continue считается плохой практикой.
  • Возможно, мы сможем добавить линты для некоторых шаблонов, которые можно было бы механически превратить в комбинаторы циклов / итераторов (хотя я не могу придумать какие-либо такие шаблоны в голове).

В качестве первого (по общему признанию предвзятого) образца моя последняя (и первая) встреча с помеченным разрывом в реальном коде не была звездной: https://github.com/rust-lang/rust/pull/48456/files#diff -3ac60df36be32d72842bf5351fc2bb1dL51. Я со всем уважением полагаю, что, если бы первоначальный автор приложил немного больше усилий, он мог бы вообще избежать использования помеченного перерыва в этом случае ... Это пример того вида практики, от которой я бы хотел отказаться, если это возможно.

Это ... без надписи "перерыв"?

Что касается того, как break и continue десахарируют, это также было упомянуто в первоначальном обсуждении RFC автором RFC; см. https://github.com/rust-lang/rfcs/pull/2046#issuecomment -312680877

Я полагаю, что имя break неоптимально, но оно хорошо зарекомендовало себя для циклов. Более «принципиальный» подход заключался бы в использовании синтаксиса return 'a value , который немедленно продолжает выполнение «после» блока 'a , используя для этого блока значение value .

@ mark-im «не использовать функцию, потому что она недоступна» не означает «сделать указанную функцию доступной».

Как мы можем настроить пометку break, чтобы я получил всю выразительную силу языка, в то же время вы перестали жаловаться на то, что ваш мозг не может обрабатывать элементы управления потоком так же, как компилятор?

(Кроме того, код, который вы связали, похоже, не имеет отношения к RFC 2046 / label-break-value ... возможно, вы связали неправильный код?)

Это ... без надписи "перерыв"?

(Кроме того, код, который вы связали, похоже, не имеет отношения к RFC 2046 / label-break-value ... возможно, вы связали неправильный код?)

Это правда, это была обычная метка продолжения, прежде чем я ее изменил, но я думаю, что существуют те же проблемы (и, возможно, еще хуже, поскольку на поток управления остальной части подпрограммы может влиять то, какое значение вы возвращаете).

@ mark-im «не использовать функцию, потому что она недоступна» не означает «сделать указанную функцию доступной».

Как мы можем настроить пометку break, чтобы я получил всю выразительную силу языка, в то же время вы перестали жаловаться на то, что ваш мозг не может обрабатывать элементы управления потоком так же, как компилятор?

Извините, я не хочу жаловаться. Если я единственный человек, который так себя чувствует, я не против уйти в сторону.

ИМХО, это фундаментальная проблема с пометкой break / continue: она слишком выразительна, и единственный известный мне способ смягчить ее - рекомендовать использование как «хороший стиль» (что бы это ни значило). Например, «используйте помеченный разрыв только со значением из начала или конца тела цикла (не из середины)». Это означало бы, что возможные способы выхода из цикла со значением легко обнаружить и обдумать.

Вот как я избегаю разрывов goto / label на других языках: https://gist.github.com/SoniEx2/fc5d3614614e4e3fe131/#file -special-lua-L4-L72

Это более читабельно?

Если так, возможно, мы сможем придумать какую-то условную систему на основе помеченных блоков. Возможно, похоже на это .

Смысл неразмеченных break и continue заключается в том, чтобы предусмотреть те случаи, когда вы не можете легко поместить условие / значение в заголовок цикла. Некоторые циклы намного проще, удобочитаемы, быстры и т. Д. С разрывом посередине.

Точка помеченных break и continue в циклах аналогична - иногда единственной альтернативой является введение новой переменной, новой функции (тем самым злоупотребляя return ) или какого-либо другого искажения, которое только усложняет отслеживание вещей, однако К сожалению, запутанный разрыв может быть.

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

Это то, что я имел в виду в вашем примере, @ mark-im - это было довольно стандартное использование помеченного цикла , а не помеченного блока .

Более «принципиальный» подход заключался бы в использовании синтаксиса return 'a value, который немедленно продолжает выполнение "после" блока' a, используя значение value для этого блока.

Боковое примечание о break vs return : я предпочитаю break потому что это статический поток управления. return является динамическим, поскольку он возвращается к вызывающему, который может быть где угодно. Это означает: «Я выполнил свои обязанности, мне больше некуда смотреть, что я делаю». break всегда находится в лексической области, как и в случае с циклами.

Я думаю, что return 'label expr действительно хорошо читается с точки зрения «делай, что я говорю», поскольку его можно рассматривать как «вернуть выражение в метку местоположения» . Я не думаю, что break 'label expr одинаково хорошо читается с этой точки зрения ...

Поэтому, в отрыве от других языков программирования, я мог бы отстаивать return 'label expr . Однако, учитывая поток управления на других языках, повторное использование return внезапно становится гораздо менее жизнеспособным вариантом, и это склоняет меня в пользу break 'label expr .

Я твердо придерживаюсь мнения, что это должно быть break 'label expr а не return 'label expr . Иначе было бы совершенно несовместимо с нашим существующим использованием циклов break 'label in.

@ SoniEx2 Я думаю, что предпочитаю опубликованный вами фрагмент, в основном потому, что переменные - хороший способ документировать инварианты цикла. OTOH. Можно было бы сделать то же самое с именами меток (т.е. каждый раз, когда вводится этот помеченный блок, сохраняется инвариант P). Думаю, это то место, где было бы неплохо получить еще несколько примеров кода из дикой природы ...

Предложение по стабилизации

Эта функция была реализована в https://github.com/rust-lang/rust/pull/50045 @ est31 и работает каждую ночь с 16 мая 2018 года (+17 недель), так что она достаточно запеклась для стабилизации. Кроме того, не было сообщений о проблемах с момента слияния ОР, реализовавшего это. С тех пор все нерешенные вопросы также были решены, и существует твердое мнение, что break должен быть поверхностным синтаксисом вместо return .

Таким образом, я перехожу к стабилизации значения разрыва метки (RFC 2046).

@rfcbot объединить

Отчет

  • Характеристики ворот:

    • https://github.com/rust-lang/rust/blob/master/src/test/ui/feature-gates/feature-gate-label_break_value.rs

    • https://github.com/rust-lang/rust/blob/master/src/test/ui/feature-gates/feature-gate-label_break_value.stderr

  • Диагностика: https://github.com/rust-lang/rust/blob/master/src/librustc_passes/diagnostics.rs#L285
  • Тесты:

    • https://github.com/rust-lang/rust/blob/master/src/test/ui/label/label_break_value_continue.rs

    • https://github.com/rust-lang/rust/blob/master/src/test/ui/label/label_break_value_unlabeled_break.rs

    • https://github.com/rust-lang/rust/blob/master/src/test/ui/label/label_break_value_illegal_uses.rs

    • https://github.com/rust-lang/rust/blob/master/src/test/ui/lint/unused_labels.rs

    • https://github.com/rust-lang/rust/blob/master/src/test/ui/run-pass/for-loop- while/label_break_value.rs

TODO перед FCP

@rfcbot проблема FIXME-in-tests

Последний тестовый файл в настоящее время имеет FIXME:

// FIXME: ensure that labeled blocks work if produced by macros and in match arms

Это должно быть решено перед стабилизацией.
Я написал несколько тестов, чтобы проверить ожидаемое поведение по отношению к. FIXME фактически реализован:

// run-pass

#![feature(label_break_value)]

#[test]
fn lbv_match_test() {
    fn test(c: u8, xe: u8, ye: i8) {
        let mut x = 0;
        let y = 'a: {
            match c {
                0 => break 'a 0,
                v if { if v % 2 == 0 { break 'a 1; }; v % 3 == 0 } => { x += 1; },
                v if { 'b: { break 'b v == 5; } } => { x = 41; },
                _ => {
                    'b: {
                        break 'b ();
                    }
                },
            }
            x += 1;
            -1
        };

        assert_eq!(x, xe);
        assert_eq!(y, ye);
    }

    test(0, 0, 0);
    test(1, 1, -1);
    test(2, 0, 1);
    test(3, 2, -1);
    test(5, 42, -1);
    test(7, 1, -1);
}

#[test]
fn lbv_macro_test() {
    macro_rules! mac1 {
        ($target:lifetime, $val:expr) => {
            break $target $val;
        };
    }
    let x: u8 = 'a: {
        'b: {
            mac1!('b, 1);
        };
        0
    };
    assert_eq!(x, 0);
    let x: u8 = 'a: {
        'b: {
            if true {
                mac1!('a, 1);
            }
        };
        0
    };
    assert_eq!(x, 1);
}
// compile-fail

#![feature(label_break_value)]

fn lbv_macro_test_hygiene_respected() {
    macro_rules! mac2 {
        ($val:expr) => {
            break 'a $val;
        };
    }
    let x: u8 = 'a: {
        'b: {
            if true {
                mac2!(2);
            }
        };
        0
    };
    assert_eq!(x, 2);

    macro_rules! mac3 {
        ($val:expr) => {
            'a: {
                $val
            }
        };
    }
    let x: u8 = mac3!('b: {
        if true {
            break 'a 3;
        }
        0
    });
    assert_eq!(x, 3);
    let x: u8 = mac3!(break 'a 4);
    assert_eq!(x, 4);
}

Перед тем, как мы перейдем от предлагаемого FCP к FCP, необходимо добавить тесты, подобные этим.

Член команды @Centril предложил объединить это. Следующий шаг - проверка остальными отмеченными командами:

  • [x] @Centril
  • [x] @aturon
  • [x] @cramertj
  • [x] @eddyb
  • [] @joshtriplett
  • [x] @nikomatsakis
  • [] @nrc
  • [x] @pnkfelix
  • [x] @scottmcm
  • [] @withoutboats

Обеспокоенность:

  • рентабельность (https://github.com/rust-lang/rust/issues/48594#issuecomment-422235234)
  • FIXME-in-tests (https://github.com/rust-lang/rust/issues/48594#issuecomment-421625182)
  • варианты использования (https://github.com/rust-lang/rust/issues/48594#issuecomment-422281176)

Как только большинство рецензентов одобрит (и никто не возражает), наступит последний период для комментариев. Если вы заметили серьезную проблему, которая не поднималась ни на одном этапе этого процесса, сообщите об этом!

См. Этот документ для получения информации о том, какие команды могут дать мне члены команды, отмеченные тегами.

@rfcbot заботится о рентабельности

Из предложения RFC FCP :

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

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

@nrc Я разделяю то же беспокойство. Я понимаю аргумент в пользу того, что он доступен, чтобы макросы могли его использовать, но в то же время я бы предпочел не иметь его вообще.

Я не хочу , чтобы RETREAD аргументы из оригинального RFC нить, но я думаю , что это разумно точка спросить об опыте работы с этой функцией. Какие применения он видел?

рукописные парсеры, в основном ... (мне нравятся мои рукописные парсеры>. <)

было бы более полезно / проще использовать с меткой-размножением ( try_foo() 'bar? ).

@rfcbot касается вариантов использования

Подводя итог некоторого обсуждения Discord: мы хотели бы увидеть конкретные варианты использования этого из реального кода, которые не будут более четко проявляться при переписывании, чтобы не использовать эту функцию.

~ FWIW, моя реализация EXPR is PAT полагается на то, что break 'label value доступен по крайней мере в AST, я не знаю, как обессахаривание могло бы работать без него.
Реализация EXPR? в компиляторе также зависит от break 'label value . ~

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

РЕДАКТИРОВАТЬ: Я полностью неверно истолковал проблему, хотя это касается loop { ... break 'label value ... } , а не блока { ... break 'label value ... } .
У меня никогда не было возможности попробовать это, потому что я всегда забываю, что он уже реализован.

@petrochenkov В разговоре с @joshtriplett на Discord они отметили, что их беспокоит сложность, с которой сталкиваются пользователи, а не реализация языка.

Я думаю, что увеличение сложности минимально: для читателей должно быть очевидно, что это означает, потому что концепция уже существует в циклах и т. Д.
В противном случае мне пришлось бы использовать цикл с оператором безусловного прерывания, который менее ясен и на самом деле есть даже обрывок (never_loop) по этому поводу. Так что я думаю, что польза есть.

Что касается вариантов использования, эта тема уже поднималась в RFC. Я указал на мой случай использования здесь . Также см. Пример использования, указанный @joshtriplett решает ли это вопрос о

Я согласен с @nrc и @joshtriplett, и я также хочу поднять здесь проблему процесса: мы предварительно приняли этот RFC с явным предупреждением о том, что стабилизация была заблокирована при повторном посещении вопросов, которые подняли @nrc и @joshtriplett , но @Centril 's Предложение о слиянии вообще не упоминает об этой проблеме блокировки и рассматривает это как очень стандартное слияние «запекшейся функции». Я не виню @Centril в этом, а в нарушении процесса: если мы собираемся предварительно принять функции с неразрешенными блокировщиками при стабилизации, нам нужно отслеживать эти блокировщики.

С точки зрения всех наших процессов меня беспокоило то, что в течение 2 с половиной дней проблема блокировки не поднималась, и большинство членов команды уже отметили свои флажки. Возможно, поскольку мы больше не требуем активного консенсуса всех участников, это могло быть введено в FCP без даже поднятия блокирующего. Это похоже на подрыв предыдущего соглашения, которое привело меня к согласию с объединением RFC, и я думаю, что это полностью вызвано недостаточным отслеживанием информации.

@withoutboats Да, именно так. Это делает меня менее склонным в будущем принимать вещи на основе принципа «мы будем XYZ в процессе стабилизации», пока у нас не будет какой-то процесс, который делает крайне маловероятным, что его пропустят.

@withoutboats

Я не виню @Centril в этом, а в нарушении процесса: если мы собираемся предварительно принять функции с неразрешенными блокировщиками при стабилизации, нам нужно отслеживать эти блокировщики.

Тем не менее, извинения; Я не знал об этом, но, тем не менее, должен был проверить наличие таких вещей, когда создавал проблему с отслеживанием.

Это похоже на подрыв предыдущего соглашения, которое привело меня к согласию с объединением RFC, и я думаю, что это полностью вызвано недостаточным отслеживанием информации.

Я должен был задать ему нерешенный вопрос; Я считаю, что в этот момент произошла ошибка процесса.
Что касается того, как улучшить процесс; Я считаю важным, чтобы нерешенные вопросы попадали в


Что касается предложения fcp-merge; Я лично считаю, что это было бы полезно по причинам единообразия и для использования макросами. Однако, если вы считаете, что еще слишком рано предлагать стабилизацию; смело отменяйте предложение :)

@ est31

В противном случае мне пришлось бы использовать цикл с оператором безусловного прерывания,

Или реструктурируйте код, чтобы избежать того же.

Я указал на мой случай использования здесь .

Почему бы не заменить break 'pseudo_return на return Ok(vectors); ?

как я уже упоминал здесь , это полезно для рукописных синтаксических анализаторов (даже без метки-размножения ( try_foo() 'bar? )).

label-break-value позволяет легко реализовать функциональный код. как правило, императивный код более читабелен, чем функциональный.

Или реструктурируйте код, чтобы избежать того же.

Конечно, Rust завершен по Тьюрингу. Но реструктуризация может быть немного сложной. В целом, вы можете отклонить почти каждую функцию сахара (и это функция сахара) на основании «вы можете просто использовать существующие способы».

Почему бы не заменить pseudo_return break на return Ok (векторы) ;?

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

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

@ est31 Я очень благодарен вам за подробности; Спасибо.

Существует проблема доступности для тестирования и использования этих вещей из-за необходимости ночного Rust.

Я нацелен на стабильную работу. Я надеюсь, что когда-нибудь это можно будет решить.

Если нам нужны варианты использования, вот тот, который я недавно рассмотрел; в основном это разработка @ est31 "выполнить обработку после": я написал лексер, который обрабатывает префиксы строковых литералов C ++ (в реальном случае C ++ - комбинаторный взрыв { L , u8 , u , U ,} { R ,}) и многобайтовые токены с «пробелами» в количестве используемых байтов (не уверен, что это имеет смысл без пример). Функция get_token настоящее время выглядит так:

fn get_token(&mut self) -> Token {
    match decode_byte(self.source) {
        // ...

        // repeat four times with small variations for b'U', b'L', and b'R':
        Some((b'u', rest)) => match decode_byte(rest) {
            Some((b'"', rest)) => self.string_literal(Utf16String, rest),
            Some((b'\'', rest)) => self.char_constant(Utf16Char, rest),
            Some((b'R', rest)) => match decode_byte(rest) {
                Some((b'"', rest)) => self.raw_string_literal(Utf16String, rest),
                _ => self.identifier(rest),
            },
            Some((b'8', rest)) => match decode_byte(rest) {
                Some((b'"', rest)) => self.string_literal(Utf8String, rest),
                Some((b'\'', rest)) => self.char_constant(Utf8Char, rest),
                Some((b'R', rest)) => match decode_byte(rest) {
                    Some((b'"', rest)) => self.raw_string_literal(Utf8String, rest),
                    _ => self.identifier(rest),
                },
                _ => self.identifier(rest),
            },
            _ => self.identifier(rest),
        },

        // ...

        // the "gap" mentioned above is here: single-byte '.' and triple-byte '...' but no double-byte '..':
        Some((b'.', rest)) => match decode_byte(rest) {
            Some((b'0'..=b'9', rest)) => self.number(rest),
            // note the _inner to avoid shadowing the outer `rest` used by the inner `Dot` case:
            Some((b'.', rest_inner)) => match decode_byte(rest_inner) {
                Some((b'.', rest)) => self.make_token(Ellipsis, rest),
                _ => self.make_token(Dot, rest),
            },
            _ => self.make_token(Dot, rest),
        },

        // ...
    }
}

Обратите внимание на пирамиды _ => self.identifier(rest) s (повторяются четыре раза для u , U , R и L ) и _ => self.make_token(Dot, rest) s, образуя своего рода стиль передачи продолжения, где identifier , string_literal и т. д. все также должны вызывать make_token .

Я хотел бы объединить вещи обратно в стиль, менее продолжительный, используя break -from-block, и почти сделал это с пометкой loop s, но считал эту версию слишком странной для чтения. Чтобы быть более конкретным:

  • Я бы переместил все вызовы make_token в одно место после основного match decode_byte(self.source) и встроил его - он небольшой и содержит unsafe с его инвариантами, поддерживаемыми get_token .
  • Я бы использовал break 'label self.string_literal(..) для короткого замыкания после нахождения " или ' , а затем объединил бы все вызовы self.identifier(..) до конца этой совпадающей руки .

    • Возможно, мне также удалось линеаризовать комбинаторный взрыв префиксов - проверьте u / u8 / U / L , затем проверьте R . Это использует меньше break 'label s, но все же горстку.

  • Я бы использовал break 'label (Ellipsis, rest) для короткого замыкания после нахождения ... , а затем объединил бы оба (Dot, rest) в конце этого ответвления.

В целом, это, по сути, «сглаживание потока управления с if + ранним возвратом» без необходимости извлечения вещей в отдельную функцию. В данном случае это очень ценно по нескольким причинам:

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

    • Или, в качестве альтернативы, закрытия, которые в некоторых из этих случаев могут вызвать проблемы с проверкой заимствований.

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

Я бы записал все это, но, полагаю, я никогда не совершал этой попытки (вероятно, потому, что я столкнулся со всеми проблемами, которые я перечислил выше), и я уже потратил здесь достаточно слов. :)

@SergioBenitez Не могли бы вы http://rocket.rs/ label_break_value и ваших взглядах на стабилизацию?

@Centril Конечно! Вот суть:

fn transform(request: &Request, data: Data) -> Transform<Outcome<_, _>> {
    let outcome = 'o: {
        if !request.content_type().map_or(false, |ct| ct.is_form()) {
            break 'o Forward(data);
        }

        let mut form_string = String::with_capacity(min(4096, LIMIT) as usize);
        if let Err(e) = data.read_to_string(&mut form_string) {
            break 'o Failure(FormDataError::Io(e));
        }

        Success(form_string)
    };

    Transform::Borrowed(outcome)
}

Используя эту функцию, я избегаю:

  • Разделение блока на другую функцию для досрочного «возврата».
  • Добавление Transform::Borrowed к каждому "возвращаемому" значению в блоке.
  • Возможная некорректность, если в разных случаях возвращается другой Transform .

    • Примечание: это инвариант, специфичный для Rocket и этого фрагмента кода.

Я был очень рад видеть, что это существует. Это именно то, что я хочу написать. При этом я ясно могу написать это по-другому, чтобы не зависеть от этой функции.

@SergioBenitez Спасибо! Интересно, сможете ли вы (в конце концов) переписать это с помощью try { .. } ? Вот так:

fn transform(request: &Request, data: Data) -> Transform<Outcome<_, _>> {
    Transform::Borrowed(try {
        if !request.content_type().map_or(false, |ct| ct.is_form()) {
            Forward(data)?;
        }

        let mut form_string = String::with_capacity(min(4096, LIMIT) as usize);
        if let Err(e) = data.read_to_string(&mut form_string) {
            Failure(FormDataError::Io(e))?;
        }

        form_string
    })
}

@Centril Да, это сработает, если есть только один путь к успеху, как здесь.

Или реструктурируйте код, чтобы избежать того же.

Делая это, вы рискуете добавить дополнительные ветки или вызовы подпрограмм, которые не могут быть разрешены оптимизатором. Мне такое преобразование потока управления кажется довольно сложной задачей.

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

Небольшая функция может быть встроена, но, тем не менее, вы получите ненужное раздувание кода или, скажем, глупое дублирование кода.

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

Интересно, сможете ли вы (в конце концов) переписать это с помощью try {..}? Вот так:
[...]

Это выглядит немного запутанным, поскольку ветки вводятся просто для оптимизации. Но в некоторых ситуациях может понадобиться и то, и другое. Таким образом, имея

'a: {try{...}}

или

'a: try {...}

было бы здорово.

Мы хотели бы увидеть конкретные варианты использования этого, из реального кода, которые не проявляются более четко при переписывании, чтобы не использовать эту функцию.

Пожалуйста, позорь меня:

Возможно, это не лучший код, но я хотел, чтобы все вообще заработало.

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

Было бы очень хорошо, если бы эта функция стабилизировалась. У меня есть множество вариантов использования, в которых этот синтаксис значительно упрощает и проясняет намерение. Для длинных разделов, связанных с выполнением ошибочных операций и разворачиванием их результатов, этот синтаксис превосходен.

Я согласен с @zesterer в том, что стабилизация этой функции принесет пользу экосистеме и сделает некоторые шаблоны менее раздражающими при написании: +1:

FWIW, я недавно впервые использовал его в компиляторе.
Шаблон тот же, что и в предыдущих примерах - мы проверяем несколько условий и прекращаем делать то, что делаем, если какое-либо из них истинно.
https://github.com/rust-lang/rust/blob/21f26849506c141a6760532ca5bdfd8345247fdb/src/librustc_resolve/macros.rs#L955 -L987

@erickt также написал код, который хотел использовать это по той же причине: проверка нескольких условий, выход из строя, когда одно из них становится ложным. Вы можете подделать это с помощью метода немедленного вызова закрытия или let _: Option<()> = try { ... None?; .. }; но оба варианта довольно хакерские.

@withoutboats @nrc @joshtriplett Есть ли у вас какие-либо мысли о вариантах использования, предложенных к настоящему времени разными людьми?

Ping @withoutboats @nrc @joshtriplett :) - на последней

Сообщение в блоге greydon подтолкнуло

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

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

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

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

Я бы подумал, что правильнее всего найти каждый ящик, в котором используется эта функция, и переписать код, чтобы в таком случае эту функцию не использовать.

@withoutboats Спасибо за то, что вы очень эффективно сформулировали многие из возражений, которых придерживаюсь и я.

Я собираюсь пойти дальше и проявить смелость здесь:

@rfcbot отменить
@rfcbot отложить

Предложение @joshtriplett отменено.

Член команды @joshtriplett предложил отложить это. Следующий шаг - проверка остальными отмеченными командами:

  • [] @Centril
  • [] @aturon
  • [] @cramertj
  • [] @eddyb
  • [x] @joshtriplett
  • [] @nikomatsakis
  • [] @nrc
  • [] @pnkfelix
  • [] @scottmcm
  • [x] @withoutboats

В настоящее время не перечислено никаких проблем.

Как только большинство рецензентов одобрит (и никто не возражает), наступит последний период для комментариев. Если вы заметили серьезную проблему, которая не поднималась ни на одном этапе этого процесса, сообщите об этом!

См. Этот документ для получения информации о том, какие команды могут дать мне члены команды, отмеченные тегами.

@withoutboats

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

Я согласен с этим принципом и думаю, что эта функция соответствует этой полосе (знакомый поток управления, ориентированный на конкретные, важные варианты использования). Было несколько раз, когда я просматривал или писал код, подобный приведенным выше примерам, когда я чувствовал, что это, безусловно, самый чистый и легкий для чтения способ написания этого кода. Повторное использование существующих конструкций потока управления, таких как loop с безусловным break в конце, вводит пользователя в заблуждение, а немедленно применяемые функции часто недостаточны из-за использования ? , await! или другие промежуточные конструкции потока управления.

Использование loop + завершающего break сбивает с толку, и вместо этого мы должны предпочесть, чтобы пользователи заявляли о своем реальном намерении, а не требовали от них злоупотребления инструментом, который был создан для другого стиля потока управления. Это похоже на то, что у нас даже есть конструкция loop , в то время как другие языки довольствуются while true { ... } . Это позволяет писать код, который выражает более четкое намерение и в результате становится более читабельным.

Вдобавок я всегда ожидал , что эта функция

TL; DR: я думаю, что эта функция поддерживает реальные варианты использования, которые могут быть записаны иначе только с помощью сильно вложенных операторов if или путем злоупотребления другими конструкциями, такими как loop-break-value. Это небольшое дополнение к поверхностному синтаксису, которое заставляет блоки вести себя так, как я ожидал, и позволяет мне писать код, который я имею в виду, а не что-то более хакерское.

@cramertj Простите, я немного запутался. Похоже, вы и @withoutboats / @joshtriplett говорите друг другу прямо противоположное?

(fwiw, я согласен с @withoutboats / @ joshtriplett)

@ mark-im Я не согласен с ними в том, что эту функцию следует закрыть. Я думаю, что его следует объединить (как указано в моих комментариях и отмеченном флажком выше).

Я согласен с @withoutboats, что мы не должны добавлять новые незнакомые инструменты, которые не мотивированы конкретными важными будет чувствовать себя знакомой и мотивирована конкретным, важными сценариями использования.

@withoutboats

Я считаю, что было немного несправедливо с моей стороны согласиться на этот эксперимент; трудно доказать отрицание, но я понятия не имею, какой тип кода мог бы преодолеть мое сопротивление добавлению этой формы потока управления в язык.

Я не думаю, что планка, которую мы установили, была «показать нам, что не может существовать никакого кода, который можно было бы лучше написать с помощью label-break-value» - это «показать нам, что конкретный код, для которого мы хотим label-break-value, может быть яснее написано по-другому ". Этот разговор начался, когда вы и другие попросили мотивирующие примеры того, где эта функция полезна, и многие люди в этой теме (включая меня) привели примеры. Они не были убедительными для вас или @joshtriplett , поэтому я теперь спрашиваю, как вы оба написали бы эти примеры без значения label-break-value. Согласны ли вы, что в примерах лучше использовать метку-разрыв-значение? Если да, то считаете ли вы, что они недостаточно распространены, чтобы перевесить затраты, связанные с предоставлением пользователям возможности писать потенциально сложный код взлома на блок?

Я собираюсь пойти дальше и проявить смелость здесь:

@rfcbot отменить

Предложение @scottmcm отменено.

Очень нерегулярно, когда разрыв выходит из блоков, только если они помечены, когда он вырывается из немаркированных петель.

Не уверен, было ли это упомянуто в потоке или нет, но блоки, не предназначенные для таргетинга с помощью break; в некотором смысле делают помеченные блоки качественно более мощными, чем помеченные loop s.

С помеченными блоками вы можете создать макрос потока управления, который поддерживает внутри него break; s, при этом макрос-инфраструктура полностью невидима для пользователя.
С помеченными циклами всегда существует риск того, что break; ярлыка будет использоваться и будет захвачен макросом, а не намеченной целью.

В моем прототипе EXPR is PAT я изначально использовал обычные loop s для обессахаривания, но что-то сломалось из-за вышеупомянутой проблемы, в частности, я не смог загрузить компилятор.
Помеченные блоки тогда еще не реализовывались, поэтому мне пришлось ввести в AST специальный «нецелевой loop » и использовать его при обессахаривании.

Как указано моим флажком, я выступаю за стабилизацию этого прямо сейчас. Я думаю, что это естественное расширение языка, которое поддерживает макросы, а также поток управления, который не так легко соответствует функциональным шаблонам. Даже несмотря на то, что NLL делает время жизни нелексическим, я думаю, что возможность аннотировать блоки с помощью времени жизни также кажется мне педагогически полезной, как и приписывание типов.

Однако, поскольку консенсуса было трудно достичь, в интересах его поиска я бы посоветовал нам попытаться ускорить работу над try { ... } а также ускорить работу над экспериментами с https://github.com. / rust-lang / rust / issues / 53667 (или одновременно с синтаксисом EXPR is PAT @petrochenkov).

Извините, в каком состоянии это сейчас? Мы в FCP или нет?

@ mark-im Поскольку ни один из ярлыков proposed-final-comment-period или final-comment-period относится к проблеме, мы не находимся в FCP или предложении по одному из них.

(и мы никогда не были в FCP, хотя было первоначальное предложение войти в FCP со всеми отмеченными флажками, кроме трех)

@scottmcm

Я собираюсь пойти дальше и проявить смелость здесь:

Я прочитал это повторение формулировки @joshtriplett как очень саркастический. Как член команды lang, пожалуйста, внимательно относитесь к тому, как вы общаетесь с другими участниками.

Я убежден, что это действительно хорошая идея. Раньше я был категорически против этой функции, и я до сих пор не думаю, что эта функция должна когда-либо использоваться программистами. Вышеупомянутые варианты использования несколько убедительны, но я думаю, что их все же можно было бы лучше разложить на более мелкие функции. Случай, который я нашел действительно убедительным, - это результат работы макросов. Если пользователь может каким-то образом вставить return , это препятствует преобразованию в функции. Однако на самом деле нам нужно просто goto . Интересно, является ли «большим» решением макросы, которые могут выводить HIR, а не токены, но это немного не так. Между тем, было бы неплохо иметь эту функцию для авторов макросов, так что в целом я думаю, что мы должны стабилизироваться.

Кто-нибудь пробовал переписывать вещи более медленным, менее интуитивным и менее эргономичным способом?

Разрушение блоков похоже на эргономичный переход. В нем почти нет проблем с goto, но он не менее эффективен.

Основываясь на изменении мнения @nrc , я снова двигаюсь к стабилизации label_break_value .
Мой отчет можно найти на https://github.com/rust-lang/rust/issues/48594#issuecomment -421625182.

@rfcbot объединить
@rfcbot проблема FIXME-in-tests

Чтобы гарантировать, что у @joshtriplett и @withoutboats есть время, чтобы

@rfcbot беспокойство дает-лодкам-и-Джош-время-поднять-любые-проблемы-они-возможно-еще-имеют

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

Член команды @Centril предложил объединить это. Следующим шагом является проверка остальными членами команды, отмеченными тегами:

  • [x] @Centril
  • [x] @aturon
  • [x] @cramertj
  • [x] @eddyb
  • [x] @joshtriplett
  • [] @nikomatsakis
  • [] @nrc
  • [x] @pnkfelix
  • [] @scottmcm
  • [] @withoutboats

Обеспокоенность:

После того, как большинство рецензентов одобрит (и не более двух утверждений остаются невыполненными), наступит последний период для комментариев. Если вы заметили серьезную проблему, которая не поднималась ни на одном этапе этого процесса, сообщите об этом!

См. Этот документ для получения информации о том, какие команды могут дать мне члены команды, отмеченные тегами.

@rfcbot проблема блокировка

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

@rfcbot разрешает вопросы, которые могут возникнуть у них.

Если это вообще имеет значение:

5 января 2019 г., 9:18:29 по тихоокеанскому времени, Маздак Фаррохзад [email protected] написал:

@rfcbot разрешить
дать-лодкам-и-Джош-время-поднять-любые-проблемы-у них-может-все еще-есть

Мы уже высказывали опасения, и они сводятся к тому, что «этого не должно быть в языке». Эти опасения никуда не денутся.

Я предпочитаю идею nrc о том, что макросы должны иметь возможность генерировать IR. Это кажется вполне разумным решением для этой и многих будущих возможностей, без необходимости добавления поверхностного синтаксиса языка.

@joshtriplett

Мы уже высказывали опасения, и они сводятся к тому, что «этого не должно быть в языке». Эти опасения никуда не денутся.

Для ясности я имел в виду регистрацию их с помощью @rfcbot потому что бот не увидит старые проблемы, зарегистрированные после того, как предложение было отменено. Я выразил это беспокойство, потому что я знаю, что у вас есть опасения, в знак любезности к вам.

Я предпочитаю идею nrc о том, что макросы должны иметь возможность генерировать IR. Это кажется вполне разумным решением для этой и многих будущих возможностей, без необходимости добавления поверхностного синтаксиса языка.

Я понятия не имею, как это будет выглядеть или что это лучшая идея; это кажется довольно спекулятивным (в смысле «это могут занять годы, прежде чем проект даже будет согласован») и более дорогостоящим, чем довольно дешевое решение break 'label expr которое также имеет преимущества, помимо того, что является результатом макроса. расширение.

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

Я, конечно, согласен с этим. Однако я не думаю, что здесь стоит беспокоиться о подходящем механизме. Беспокойство похоже на то, что «если бы это было решено, я бы одобрил». Здесь проблема в том, что «это вообще не должно продвигаться вперед, 'merge' - неправильная цель, я бы вместо этого хотел 'close'». Кажется, с этим не лучше всего справиться с помощью механизма «озабоченности».

@joshtriplett В качестве интереса, не могли бы вы направить меня на поднятые вами проблемы, о которых вы упомянули ранее? Я просмотрел исходную ветку RFC и не уверен, о чем конкретно идет речь. Спасибо.

@rfcbot касается эргономики и оптимизации / производительности (пример снижения производительности и эргономики без ржавчины https://gist.github.com/SoniEx2/fc5d3614614e4e3fe131#file-special-lua)

я не знаю, если я использую это правильно

(обратите внимание, как некоторые из "обессахаривания" (на самом деле я бы сказал, что это полная противоположность обессахаривания - goto - примитив, переход от desugar к goto) довольно ужасен)

(также обратите внимание, что в lua нет ничего помеченного, поэтому он вынуждает вас использовать goto. даже в этом случае, я считаю, что по-прежнему потребуется хотя бы один блок останова, но, вероятно, с более чистым «обессахариванием».)

@rfcbot беспокоиться о том, что нужно закрывать, а не объединять

(По предложению @centril на Discord.)

Я не уверен, является ли это аргументом в пользу стабилизации или против, но обратите внимание, что сегодня это тривиально легко закодировать в стабильном Rust:

fn main() {
    'foo: for _ in 0..1 {
        println!("break");
        break 'foo;
        println!("broken");
    }
}

Так что, с одной стороны, мы должны просто стабилизировать это, потому что это едва ли развивает язык, просто исключая for _ in 0..1 , с другой стороны, мы не должны стабилизировать это, потому что есть простой способ сделать это сегодня (когда это действительно необходимо), и мы не должны поощрять использование такого антипаттерна.

это кажется немного запутанным, и этикетка не нужна.

Разрушение блоков намного менее запутанно и требует ярлыка.

Я не понимаю, почему вы думаете, что разбивание блоков - это антипаттерн. то, что применимо к X, не обязательно применимо к не совсем-X. Я могу купить компьютер за 299,99 доллара, но не за 299,98 доллара, даже если они «в основном» одинаковы.

@nrc

Я не уверен, является ли это аргументом в пользу стабилизации или против, но обратите внимание, что сегодня это тривиально легко закодировать в стабильном Rust:

    'foo: for _ in 0..1 {
        break 'foo;
    }

Итак, с одной стороны, мы должны просто стабилизировать это, потому что это едва ли развивает язык, просто исключая for _ in 0..1 ,

'a: for _ in 0..1 { break 'a } может сработать, если явная цель состоит в том, чтобы никто никогда не писал 'a: { ... break 'a e; ... } ; тем не менее, у него есть недостаток, заключающийся в генерации мусорного IR, который должна обрабатывать программа проверки типов, MIR и LLVM, что приводит к ухудшению времени компиляции (по крайней мере, по сравнению с LBV).

с другой стороны, мы не должны стабилизировать это, потому что есть простой способ сделать это сегодня (когда это действительно необходимо), и мы не должны поощрять использование такого антипаттерна.

Я думаю, мы не согласны с тем, что LBV - это антипаттерн. В конце концов, мы решили наполнить Rust императивным потоком управления, а не просто сделать Rust функциональным языком программирования. Хотя я, возможно, предпочел бы не иметь такого потока управления, это свершившийся факт. Тогда возникает вопрос, является ли LBV более нечитаемым или проблематичным, чем другие императивные механизмы в Rust. Я так не думаю.

Хотя я считаю, что функции должны быть короткими (по вертикали) и неглубокими (отступы), лучше быть длинными и неглубокими, чем длинными и глубокими. Я считаю, что LBV помогает избежать глубины в некоторых из более сложных потоков. LBV также кажется мне читабельным, поскольку он явно указывает, куда он перейдет, что делает поток понятным. В целом, я считаю LBV одним из наименее проблемных потоков императивного управления.

  1. 'a: for _ in 0..1 { break 'a }
  2. 'a: { ... break 'a e; ... }

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

вроде как loop {} vs while true {} (предоставлено loop может возвращать значение, в то время как while не может, но давайте проигнорируем это немного)

@nrc Это не совсем то же самое поведение: https://github.com/rust-lang/rust/issues/48594#issuecomment -450246249

В частности, поведение break; и continue; отличается. Рассмотрим этот гипотетический код:

some_macro! {
   ...
   break;
   ...
}

Если some_macro расширяется до 'a { ... } , это ведет себя иначе, чем если бы оно расширялось до 'a: for _ 0..1 { ... }

@ SoniEx2

Кто-нибудь пробовал переписывать вещи более медленным, менее интуитивным и менее эргономичным способом?

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

Возможно, я столкнулся с каким-то большим недоразумением. Я не буду претендовать на то, чтобы быть экспертом по LLVM, внутреннему устройству Rust или подобным вещам. Тем не менее, у меня есть элементарный опыт проектирования компиляторов, и я не понимаю, в чем заключается проблема. Если бы кто-то мог что-то объяснить, я был бы очень признателен.

То, как я вижу вещи:

1) Это не меняет эргономики управления потоком. Подобные конструкции уже существуют в Rust, например 'a: loop { if x { break 'a; } break; } .

2) Это не особо меняет эргономику возврата блока: циклы уже могут возвращать значения.

Для меня это больше похоже на интуитивное завершение набора функций, которые уже есть в Rust, - обобщение этих функций для всех (или, по крайней мере, большего количества) блоков.

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

Может ли кто-нибудь более конкретно объяснить мне, какие проблемы существуют?

С процедурной точки зрения, IMO не подходит для подачи жалоб на concern blocking или concern should-close-not-merge -стайл: эта проблема не перечисляет проблему с предложенной функцией или что-либо, что мы могли бы работать над решением - - это просто перманентный блокатор. Я не думаю, что механизм беспокойства rfcbot следует использовать для функций постоянной блокировки, но только для того, чтобы помочь отслеживать решение проблем и убедиться, что они решаются / решаются надлежащим образом. Насколько я понимаю, этот процесс заключался в том, чтобы использовать снятый флажок, чтобы отметить ваше несогласие, и что проблемы должны были использоваться для решения конкретных обсуждаемых вопросов, касающихся функции и ее функциональных возможностей.

Я мог бы подать подобные «мне это не нравится» по поводу других функций, с которыми я лично не согласен (см., Например, uniform_paths), но я не верю, что бесконечный пират является продуктивным способом создания языкового дизайна.

Если есть особые опасения по поводу того, как эта функция взаимодействует с другими частями языка, которые следует обсудить / решить (например, «похоже, что этого можно избежать с помощью try { ... } » или «это помогает включить отключение»). идиоматически-большие функции ») Думаю, продуктивнее было бы их так запилить.

@cramertj Я бы не стал @Centril не предложил это прямо. (Я лично предпочел бы, чтобы FCP не предлагался.)

Что бы вы предложили в качестве подходящего процесса для «это должно быть закрыто», если кто-то подал P-FCP с rfcbot для чего-то другого, кроме «закрыть»? «Убедитесь, что они решены / разрешены надлежащим образом» звучит эквивалентно «когда-нибудь это стабилизируется, что нужно для этого?». Это не оставляет пути в потоке, поскольку «это никогда не должно стабилизироваться, это не должно быть частью языка».

Насколько я понимаю, этот процесс заключался в том, чтобы использовать снятый флажок, чтобы отметить ваше несогласие.

Затем нам нужно будет снова изменить процесс, чтобы для продолжения нужно было установить все флажки.

Я не верю, что бесконечный пират - это продуктивный способ языкового дизайна.

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

Для протокола, основываясь на опыте работы с этим RFC, я не думаю, что снова будет хорошей идеей отвечать на RFC с оговоркой «мы можем оценить / решить во время стабилизации, должны ли мы продолжать», потому что мне это кажется ясным. это приводит к возникновению подобных критических процедурных проблем.

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

Я повторю то, что сказал раньше, потому что, похоже, это было упущено: https://github.com/rust-lang/rust/issues/48594#issuecomment -451795597

Я в основном говорил о различиях между использованием цикла и использованием прерывающего блока.

в основном блоки разрыва имеют разный синтаксис, (немного) разную семантику (по крайней мере, в отношении немаркированного прерывания), они означают разное намерение и несколько других второстепенных вещей.

rfcbot поддерживает антиконцерны?

@joshtriplett

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

Да, в нашем текущем процессе есть досадная опасность, что принятие или отклонение функции требует полного согласия соответствующей команды. Когда команда lang так велика, как сейчас, всегда трудно добиться этого - я подумал из комментариев выше, что здесь просто отсутствовали примеры кода, чтобы мотивировать, почему эта функция важна, но теперь похоже, что вы согласны с тем, что есть - это код, который можно лучше написать с помощью этой функции, но он не уверен, что он стоит тех затрат, которые вы видите здесь. Я не уверен, как убедить вас, что эти случаи являются достаточной мотивацией, поскольку кажется, что продолжения предоставления примеров (включая пример макроса, который буквально невозможно написать в другом стиле) недостаточно.

Точно так же я довольно уверен, что лично я останусь убежденным, что эту функцию следует объединить: ИМО, она не только тянет за собой множество примеров, где это лучший / единственный вариант, но и язык на самом деле проще с его добавлением (так как не позволяет меченых блоков удивительно для меня , учитывая , что мы позволяем другим меченые конструкции).

Если вы согласны с изложенным выше изложением вашей позиции, то, похоже, мы находимся в прискорбном (и, я считаю, историческом!) Тупике, в котором отсутствует процесс. Один из вариантов - интерпретировать текущие правила rfcbot, как я сделал выше, как «отсутствие флажка сигнализирует о вашем несогласии, три члена необходимы для отмены большинства», но я не думаю, что правила имели в виду, когда они были введены, таким образом, поэтому Думаю, с моей стороны было лукавством предложить вам следовать этой процедуре (хотя я сам делал это в другом месте).

Ранее я слышал предложения о том, что мы могли бы ввести ограничение по времени для функций, переходящих от утвержденных-> реализованных-> стабилизированных, и что мы должны «автоматически закрывать» функции, которые отстают, чтобы избежать постоянно увеличивающегося отставания. Что-то подобное могло бы решить этот случай, когда я (и, я думаю, несколько других в команде) не буду отмечать флажки для закрытия, а вы не ставите флажки для принятия (я все еще боюсь даже сказать это, что я '' Я бросаю последнюю попытку убедить вас!: smile :).

Меня беспокоит, что с постоянно растущими командами мы потеряем способность прийти к консенсусу по функциям, и что будет трудно согласованно управлять языком, особенно в команде разработчиков языков. Ограничение размера команды кажется очевидным решением этой проблемы - можно получить информацию от большого количества людей, но совершенно невозможно достичь абсолютного консенсуса среди достаточно большой группы. Другие, вероятно, будут утверждать, что спорные функции не следует объединять, чтобы защитить язык от ошибок. Я лично думаю, что сообщество вряд ли позволит нам совершить многие из этих ошибок без надлежащего предупреждения, но это мысль.

Я попытаюсь создать отдельную ветку, чтобы обсудить здесь эволюцию процесса, а пока я попрошу людей, участвующих в этой теме, публиковать сообщения только в том случае, если есть новые, критические варианты использования, которые следует учитывать, которые заметно отличаются от те, что указаны выше, или если есть серьезная неучтенная причина, по которой эту функцию не следует добавлять в язык. Прочитать всю историю по этим мегапотокам сложно, но становится еще труднее, когда сообщения повторяются многократно или когда цепочка заполнена бесполезными комментариями. (он говорит, что набрав один из самых длинных комментариев, грешит на всю ветку XD)

TL; DR: Я думаю, что мы застряли, у нас должен быть процесс для этого - я начну этот разговор в другом месте и свяжу его здесь. В противном случае, пожалуйста, не комментируйте, если у вас нет важной новой информации, которую необходимо учитывать.

@cramertj

Прискорбная опасность нашего текущего процесса состоит в том, что принятие или отклонение функции требует полного согласия соответствующей команды

Я честно считаю это особенностью.

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

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

(Также стоит отметить: ящик @nrc proc-macro-rules который использовал label_break_value похоже, был переписан, чтобы этого избежать.)

Один из вариантов - интерпретировать текущие правила rfcbot, как я сделал выше, как «отсутствие флажка сигнализирует о вашем несогласии, три члена необходимы для отмены большинства», но я не думаю, что правила имели в виду, когда они были введены, таким образом, поэтому Думаю, с моей стороны было лукавством предложить вам следовать этой процедуре (хотя я сам делал это в другом месте).

Я считаю, что это правильная процедура для «воздержания», но не для возражения.

IMO он не только тянет свой вес через множество примеров, где это лучший / единственный вариант, но и язык на самом деле проще с его добавлением (поскольку запрет на использование помеченных блоков меня удивляет, учитывая, что мы разрешаем другие помеченные конструкции).

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

Меня беспокоит, что с постоянно растущими командами мы потеряем способность прийти к консенсусу по функциям, и что будет трудно согласованно управлять языком, особенно в команде разработчиков языков.

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

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

@joshtriplett

Это не добавляет языку непредсказуемой выразительности.

Чтобы быть предельно ясным: он делает именно это. В настоящее время нет способа выразить этот код, который не мешал бы другим конструкциям потока управления, что (как указывали другие) особенно желательно в макросах, где это была бы единственная конструкция, которая не является целевой посредством немаркированных разрывов, позволяя авторам макросов выходить из разделов, не рискуя пересечься с предоставленным пользователем break .

это также делает код более читаемым, так как вам не нужно двигаться зигзагами (если бы у нас вообще не было ярлыков - см. пример Lua) или делать странные вещи с циклами. это также дает небольшое преимущество в производительности, даже если llvm сможет оптимизировать зигзагообразный код.

@joshtriplett

Я все еще считаю, что многие примеры _ можно_ написать по-другому. Это не добавляет языку непредсказуемой выразительности. Первоначально я чувствовал, что это может быть мотивировано достаточным количеством примеров, но чем больше я вижу примеров, которые _ можно_ использовать эту функцию, тем больше я соглашаюсь с @withoutboats, что эта функция просто не должна входить в язык.

Возможно, это то, во что мы могли бы покопаться?

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

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

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

Еще нет его собственной формы «нет» в том смысле, что без «да» он не стабилизируется. Кроме того, нет: убедить остальных в том, что LBV - плохая идея / недостаточно мотивированная по причинам X, Y и Z. Таких конкретных аргументов мне еще не приходилось слышать. Вы также ясно продемонстрировали здесь, что нет неизбежности.

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

Я лично считаю , что многие из этих сообщений либо об устойчивости (но не так точно сформулированы @nikomatsakis сделал ...) или , что многие люди не понимают , как язык команда работает (то есть мы уже делают уделять серьезное внимание к общему площадь поверхности). Общий поверхностный синтаксис Rust, вероятно, фактически сократился за последний год, а не вырос. LBV не увеличивает это заметно, и можно привести аргумент, что он фактически сокращает количество производимых на языке текстов и делает синтаксис более единообразным.

Простое комбинирование грамматических произведений не сужает поверхность языка.

Уменьшение количества условных ветвей, которые можно сделать, вероятно, сужает площадь поверхности языка.

Я не думаю, что эта особенность идет ни в коем случае, как таковая, вероятно, нейтральна. Но, например, вещи, которые объединяют языковые конструкции (ну, пусть кто-нибудь?) , Действительно сокращают площадь поверхности.

Как бы то ни было, Common Lisp, похожий на Rust в том смысле, что это многопарадигмальный язык с макросами, имеет эту функцию (названную block / return-from ). Подобно тому, что обсуждалось здесь, в Common Lisp эта конструкция часто бывает полезной при написании макросов и при выражении неснижаемо сложного потока управления.

(block foo
  (return-from foo "value"))

Мне кажется, что в Common Lisp эта функция считается успешной. Это не касается разговоров о функциях, которые затрудняют изучение или реализацию языка.

Есть включение

early return from block ⊂ exceptions ⊂ call/cc

Например, в схеме возможна следующая эмуляция return-from :

(define call/cc call-with-current-continuation)

(define-syntax block
    (syntax-rules ()
        ((_ label statements ...)
            (call/cc (lambda (label) (begin statements ...))))))

(block foo
    (display "Visible text")
    (foo "value")
    (display "Phantom text"))

В схеме call/cc рассматривается как успешное, так и спорное. Я нахожу это особенно интересным, потому что сначала нужно сформулировать объект, чтобы иметь возможность говорить о нем. Даже если вы сочтете call/cc ошибкой, это сделало язык более содержательным в интеллектуальном смысле.

В языке именованных блоков .
Это очень полезно и удобно.

return, break и continue реализованы в виде макросов с использованием этой функции.

@jhpratt Прочтите, пожалуйста, обсуждение вопроса.

Повторяю некоторые комментарии, которые я сделал об IRLO по предложению @Centril :

Обсуждая эту функцию, я предложил следующее (в отличие от обычной версии, связанной с циклами):

Во всяком случае, петли здесь не совсем мое вдохновение. Когда я смотрю на блоки try, я думаю, что «было бы полезно написать несколько коротких локальных вычислений с ранним возвратом, не прибегая к трудностям по созданию замыкания». Блоки try работают с довольно ограниченным выражением «Я хочу вернуть impl Try» и не поддерживают никаких меток.

Вышесказанное является моим основным вариантом использования для этого: мне нравится стиль раннего возврата. try {} блокирует нам большую часть пути, но, опять же, они на самом деле не поддерживают метки (не то, что вам нужно, потому что вы напишете try {}? для вложенных блоков) и сделаете вы впихиваете свой тип в определенную монадическую форму, которая не является универсальной (о чем свидетельствуют предоставленные варианты использования).

Я также сделал несколько замечаний по поводу C ++, где отсутствие меток break / continue очень болезненно, особенно при отсутствии итераторов, подобных Rust. Например, невозможно продолжить внешний цикл из внутреннего цикла без возможно-UB goto или локального break плюс условного continue вне внутреннего цикла.

По крайней мере , C ++ отсутствие 's из заема-проверки делает встроенный лямбда - трик менее болезненным, что делает, по сути, поддержка LVB, после возвращения в инлайн лямбда преобразуемый что - то вроде LVB по LLVM в вкладки. Что-то вроде этого ... немного более сомнительно в Rust.

Я также должен отметить, что эта функция примерно эквивалентна по выразительности Go goto , которая во время компиляции заставляет не перепрыгивать через объявления и тому подобное (честно говоря, если Роб Пайк считает, что goto приемлемо, учитывая история попыток решить проблемы C ++, я бы доверился ему в этом вопросе).

Кроме того, если мы собираемся обратиться к известному уровню техники, Kotlin также предоставляет эту точную функцию в виде return<strong i="24">@label</strong> expr; .

В языках c'ish я часто использую метки, даже без входящего goto, как места для стабильных точек останова в циклах, потому что отладчик может распознать break function:label .
Даже без единого мнения о разрыве ярлыки могут быть хорошими.

Изменить: одно потенциальное препятствие заключается в том, что обычно метки следуют соглашению об именах символов, если я понимаю RFC, эти метки не соответствуют соглашению об именах символов, где ', недопустимо в имени символа.
Я не уверен, например, в Dwarf или самом gdb, если здесь действительно есть какие-то проблемы.

Edit2:
Совершенно уверен, что в этом есть какой-то дым, если мы посмотрим на поведение цитирования обычных меток на основе c,
в отладчике gdb, по крайней мере, будет обрабатывать кавычки для цитирования, а не для части имени символа. Следующие результаты в

Точка останова 1 по адресу 0x401114: файл, линия 1.
непревзойденная цитата

echo "void main() { } void hmm() { umm: return; }" | gcc -g -x c -;
gdb -ex "b hmm:'umm'" -ex "b hmm:'umm" -batch ./a.out

И я не верю, что на это может повлиять поддержка языка ржавчины в gdb, поскольку я считаю, что это цитирование происходит до сопоставления символов.

Изменить: корабль, вероятно, плыл на этом из-за существующих меток петель.

Второстепенным моментом в пользу досрочного возврата из блоков было бы программирование контракта для бедняков. Один просто добавляет утверждения assert в качестве предусловий и постусловий. Для обеспечения комфорта должна быть возможность заменить return на break 'ret чтобы разрешить эту конструкцию:

let value = 'ret: {
    // ...
};
assert!(postcondition(value));
return value;

Однако это несовершенное решение, потому что return следует запретить внутри блока.

Добавление примечания, что я хотел эту функцию, но не использовал ее, потому что я не знал, что она существует, что я считаю отрицательным модификатором на «люди не хотят ее, о чем свидетельствует то, как мало людей используют ее».

Сегодня утром я независимо

Я сегодня поигрался с этим, и он приходит _super_ удобно в блоках async . Однако, похоже, возникают проблемы в сочетании с функцией try_blocks :

#![feature(try_blocks, label_break_value)]

fn main() {
    let _: Result<(), ()> = try {
        'foo: {
            Err(())?;
            break 'foo;
        }
    };
}
error[E0695]: unlabeled `break` inside of a labeled block
 --> src/main.rs:6:20
  |
6 |             Err(())?;
  |                    ^ `break` statements that would diverge to or through a labeled block need to bear a label

error: aborting due to previous error

блоки try являются ошибкой.

... не можете ли вы пометить сам ? ? (как в Err(()) 'foo?; )

Я категорически не согласен с тем, что блоки try являются ошибкой, хотя это отдельное обсуждение и, вероятно, не стоит здесь возвращаться и возвращаться.

В этом конкретном примере это может быть выполнимо, но это очень минимизировано по сравнению с реальным кодом, который у меня есть, где 'foo содержит приличный кусок кода и несколько ? s.

@ SoniEx2

блоки try являются ошибкой.

Этот комментарий неуместен. Комментарий @jonhoo сообщал о (предположительно) некорректном взаимодействии. Независимо от мнения о блоках try (или label-break-value) ясно, что они должны взаимодействовать без сбоев.

они должны, используя синтаксис Err(()) 'foo?; .

@jonhoo Я подозреваю, что вы видите, как просачиваются подробные сведения о том, как избавляется от сахара в try - можете ли вы зарегистрировать это как отдельную проблему, и мы можем перенести туда обсуждение возможных исправлений?

RFC говорит, что

'BLOCK_LABEL: { EXPR }

синтаксический сахар для

'BLOCK_LABEL: loop { break { EXPR } }

Я попытался сделать эту замену, и код компилируется с предупреждением о недоступности кода.

#![feature(try_blocks, label_break_value)]

fn main() {
    let _: Result<(), ()> = try {
        'foo: loop {
            break {
                Err(())?;
                break 'foo;
            }
        }
    };
}

@nikomatsakis @ciphergoth подано как https://github.com/rust-lang/rust/issues/72483.

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

(Это относится к текущей форме с использованием break , а не к любому другому синтаксису.)

@rfcbot разрешает закрытие, а не слияние
@rfcbot просмотрел

@joshtriplett Как бы то ни было, я обнаружил, что это чрезвычайно полезно в блоках async , поскольку это единственный способ сделать "ранний возврат". Это означает, что вместо написания:

async {
  // do thing a
  if thing_a_failed {
    // handle specially (note, _not_ ?)
  } else {
    // do thing b
    if thing_b_failed {
      // handle specially (note, _not_ ?)
    } else {
      // do thing c, etc..
    }
  }
}

Я могу написать:

async {
  'block {
  // do thing a
  if thing_a_failed {
    // handle specially (note, _not_ ?)
    break 'block;
  }

  // do thing b
  if thing_b_failed {
    // handle specially (note, _not_ ?)
    break 'block;
  }

  // do thing c, etc..
  }
}

Это полностью аналогично тому, как вы можете досрочно вернуться с return в функциях / замыканиях и с continue/break в циклах. По общему признанию, было бы неплохо, если бы мне не понадобился дополнительный блок ( async 'block { возможность?), Но он определенно превосходит вложенные if-s.

Разрешение аннотировать асинхронные блоки метками - это хорошее расширение этой возможности.

@rfcbot fcp отменить

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

Предложение @nikomatsakis отменено.

Обратите внимание, что нет двусмысленности в семантике этого предложения при наличии асинхронных блоков: определение в RFC по-прежнему применяется, т.е.

'BLOCK_LABEL: { EXPR }

просто синтаксический сахар для

'BLOCK_LABEL: loop { break { EXPR } }

за исключением того, что непомеченные перерывы или продолжения, которые будут привязаны к неявному циклу, запрещены внутри EXPR.

Обратите внимание, что вы можете (раньше) return из асинхронных блоков вместо помеченных break , поэтому маркировка асинхронных блоков не имеет особого смысла:

let fut = async {
    return 42;
    0
};

println!("{}", fut.await); // prints 42

( детская площадка )

@WaffleLapkin Я на самом деле только что пришел сюда, чтобы отметить, что, как я сам недавно был проинформирован об этом ! Я действительно думаю, что эта функция по-прежнему очень полезна для возможности _пропускать_ разделы кода (не запускать остальную часть этого блока, но также не возвращать), но ее применимость для async _specifically_ меньше, чем Я изначально подумал.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги