Rust: Rastreamento de problema para RFC 2046, valor de quebra de rótulo

Criado em 27 fev. 2018  ·  135Comentários  ·  Fonte: rust-lang/rust

Este é um problema de rastreamento para RFC 2046 (rust-lang / rfcs # 2046).

Degraus:

Perguntas não resolvidas:

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

Comentários muito úteis

@withoutboats

Eu gostaria de evitar a adição de opções mais amplamente aplicáveis ​​e altamente flexíveis à caixa de ferramentas de fluxo de controle de Rust. Estou interessado apenas em adicionar fluxo de controle voltado para casos de uso importantes e específicos e que tenha um alto impacto ergonômico.

Eu concordo com esse princípio e acho que esse recurso atende a essa barreira (fluxo de controle familiar direcionado a casos de uso importantes e específicos). Várias vezes, quando revisei ou escrevi código como os exemplos fornecidos acima, sinto que essa é de longe a maneira mais limpa e fácil de ler de escrever esse código. Reutilizar construções de fluxo de controle existentes como loop com um break incondicional no final engana o usuário, e as funções aplicadas imediatamente são frequentemente insuficientes devido ao uso de ? , await! ou outras construções de fluxo de controle intermediárias.

Usar loop + trailing break é confuso, e devemos preferir que os usuários declarem sua real intenção em vez de exigir que abusem de uma ferramenta feita para um estilo diferente de fluxo de controle. Isto é semelhante ao fato de que temos até um loop construto, enquanto outros idiomas estão contentes com while true { ... } . Isso torna possível escrever um código que expressa uma intenção mais clara e, como resultado, é mais legível.

Além disso, esse recurso é algo que eu sempre esperei que Rust tivesse, visto que rotulamos a maioria das outras coisas e quebramos das coisas rotuladas, o fato de que você não pode rotular ou quebrar um bloco parece confuso e errado para mim.

TL; DR: Eu acho que este recurso está dando suporte a casos de uso do mundo real que só podem ser escritos de outra forma por meio de instruções if fortemente aninhadas ou abusando de outras construções como valor de quebra de loop. É um pequeno acréscimo à sintaxe de superfície que faz com que os blocos se comportem como eu esperava que se comportassem e me permite escrever o código que quero dizer, em vez de algo muito mais hackeado.

Todos 135 comentários

Usar "return" teria implicações interessantes para ? rotulado (tryop questionmark operator thingy).

Use return como palavra-chave em vez de break?

@mark-im e @joshtriplett já se manifestaram contra o retorno, mas vou juntar-me a mim, visto que ainda é uma questão aparentemente não resolvida.

break (e continue) só podem ser usados ​​com um loop.
Se você pode quebrar algo, você pode continuar. (Não acho que haja uma sintaxe óbvia a ser escolhida para continuar em um bloco.)

Em C, C ++, Java, C #, JavaScript e provavelmente em mais linguagens, você normalmente break ing uma instrução switch para evitar falhas. A ferrugem resolveu isso muito melhor com | em padrões, mas as pessoas que vêm dessas línguas não verão realmente break como algo apenas para loops for. Especialmente porque Java e JavaScript expõem o recurso por meio de break e não de return .

O argumento das "regras para lembrar" funciona muito bem na outra direção, entretanto. Pelo que posso dizer, é uma comunalidade das linguagens mencionadas, assim como Rust, que o retorno só se aplica a funções e nada mais. Portanto, se você ver um retorno, sabe que uma função está sendo deixada.

Rotular um bloco não causa erros nas quebras não rotuladas existentes

Em primeiro lugar, acho que isso acontece muito raramente, já que o recurso de quebra rotulado não é algo que será usado 10 vezes a cada 1000 linhas. Afinal, só se aplicará a quebras não marcadas que cruzariam os limites do bloco, e não a quebras não marcadas dentro do bloco. Em segundo lugar, os usuários do Rust estão acostumados a reclamações / mensagens de erro do compilador, eles ficarão felizes em corrigi-los! Terceiro (este é o ponto mais forte, eu acho), se em vez de rotular um bloco você embrulhá-lo em um loop, você já precisa estar atento para quebras não rotuladas e não há mensagem de erro que liste convenientemente as instruções break, você tem para caçá-los você mesmo :).

Principalmente porque Java e JavaScript também expõem o recurso via break e não retorno.

Isso para mim é o ponto crucial. quebrar de blocos é uma coisa em muitas línguas. retorno de blocos ... nem tanto.

Pessoalmente, compartilho a opinião de break vez de return , mas me pareceu que a discussão não havia sido resolvida no RFC ... Se você acredita na questão é resolvido na equipe lang, marque a caixa com uma nota =)

Só estou dizendo que estou trabalhando nisso. Não precisa de instruções do mentor. Apenas para não duplicar esforços. Espere um PR em breve.

Ainda sou a favor de return vez de break , mas posso concordar em discordar aqui. Questão resolvida.

Atualmente (com rustc 1.28.0-nightly (a1d4a9503 2018-05-20) ) rustc não permite unsafe em um bloco rotulado. Isso é esperado?

@topecongiro Sim, acredito que seja intencional que atualmente seja permitido apenas em blocos simples. Isso pode mudar no futuro, mas como esse é um recurso incomum e de baixo nível, estou inclinado a considerá-lo um recurso em vez de uma restrição. (No extremo, certamente não quero else 'a: { .)

Definitivamente concordo. Fluxo de controle inseguro + incomum soa como algo para desencorajar.

Em uma pitada, porém, você poderia usar:

'a: {
unsafe {...}
}

Direito?

Na verdade, embora outra coisa crie um novo escopo léxico, não é um bloco. Todo o if-else é um bloco (meio). Portanto, não, você não obteria else 'a: { você obteria 'a: if ... else { .

else contém um bloco (expressão). não há "novo escopo léxico" sem blocos.
Uma posição de sintaxe de superfície ainda pior do que else seria 'a: while foo 'b: {...} .
(curiosamente, continue 'a é break 'b , podemos querer confiar nisso pelo menos internamente)

(curiosamente, continue 'a é break 'b , podemos querer confiar nisso pelo menos internamente)

Essa é uma ótima observação!

Acho que os rótulos devem fazer parte das expressões que contêm blocos, e não os próprios blocos. Já temos precedentes para isso com loop . (Acontece que um bloco simples em si também é uma expressão que contém blocos. Mas coisas como if e loop são expressões que contêm blocos sem serem blocos, eu acho.)

(Coisas como while ou for não devem suportar o valor de quebra de rótulo, porque eles podem ou não retornar um valor com base no fato de serem concluídos normalmente ou com um break .)

@eddyb

(curiosamente, continue 'a is break' b, podemos querer confiar nisso, pelo menos internamente)

Somente se break 'b verificar novamente a condição do loop ...

@ mark-im É equivalente a 'a: while foo {'b: {...}} , o break não checaria a condição de loop, o próprio loop faria, porque a condição de loop é verificada antes de cada iteração do bloco de corpo.

Uau, acho isso _altamente_ pouco intuitivo. Espero que break 'b seja basicamente goto 'b , o que significa que nunca saímos do corpo do loop e a condição não é verificada novamente ...

Oh: man_facepalming: Entendo ...

É por isso que não gosto do rótulo break / continue: /

Bem, especificamente não temos a capacidade de rotular esses estranhos bloqueios internos, então não vejo o problema. break sempre significa "sair deste bloco" e, dada a restrição acima, não há outra maneira de significar outra coisa senão "vá para o local após a chave de fechamento associada."

Minha confusão não era específica para estranhos bloqueios internos, mas eu realmente não quero reabrir a discussão. Isso já aconteceu e a comunidade decidiu adicioná-lo.

Ok, eu entendo que a acessibilidade é um grande problema com linguagens de programação ... no entanto, break é extremamente útil se você escrever código como eu.

Então, como podemos tornar o intervalo rotulado mais acessível?

Então, como podemos tornar o intervalo rotulado mais acessível?

Essa é uma ótima pergunta. Algumas ideias que tive:

  • Devíamos coletar algumas amostras de como as pessoas usam isso na natureza. Podemos procurar padrões indesejáveis ​​ou hábitos preguiçosos.
  • Devemos ter um estilo opinativo sobre quando é considerado uma prática ruim usar interromper / continuar rotulado.
  • Podemos adicionar lints para alguns padrões que podem ser transformados em combinadores de loops / iteradores mecanicamente (embora eu não consiga pensar em nenhum desses padrões de cabeça).

Como uma primeira amostra (reconhecidamente tendenciosa), meu último (e primeiro) encontro com a quebra rotulada no código real não foi estelar: https://github.com/rust-lang/rust/pull/48456/files#diff -3ac60df36be32d72842bf5351fc2bb1dL51. Respeitosamente, sugiro que, se o autor original tivesse se esforçado um pouco mais, ele poderia ter evitado o uso de quebra rotulada nesse caso ... Este é um exemplo do tipo de prática que eu gostaria de desencorajar, se possível.

Isso não é ... rotulado de pausa?

Em relação a como break e continue desugar nisso, isso também foi mencionado na discussão original do RFC pelo autor do RFC; consulte https://github.com/rust-lang/rfcs/pull/2046#issuecomment -312680877

Suponho que o nome break seja o ideal, mas está bem estabelecido para loops. Uma abordagem mais "baseada em princípios" seria usar a sintaxe return 'a value , que imediatamente continua a execução "após" o bloco 'a , usando o valor value para esse bloco.

@ mark-im "não está usando um recurso porque não está acessível" não está "tornando esse recurso acessível".

Como podemos ajustar o intervalo rotulado para que eu obtenha todo o poder expressivo da linguagem e, ao mesmo tempo, você pare de reclamar que seu cérebro não pode processar coisas de controle de fluxo tão bem quanto o compilador?

(Além disso, o código que você vinculou não parece ter relação com RFC 2046 / label-break-value ... talvez você tenha vinculado o código errado?)

Isso não é ... rotulado de pausa?

(Além disso, o código que você vinculou não parece ter relação com RFC 2046 / label-break-value ... talvez você tenha vinculado o código errado?)

Isso é verdade, era um normal rotulado continue antes de eu alterá-lo, mas acho que os mesmos problemas existem (e são possivelmente piores, já que o fluxo de controle do resto de uma rotina pode ser afetado pelo valor que você retorna).

@ mark-im "não está usando um recurso porque não está acessível" não está "tornando esse recurso acessível".

Como podemos ajustar o intervalo rotulado para que eu obtenha todo o poder expressivo da linguagem e, ao mesmo tempo, você pare de reclamar que seu cérebro não pode processar coisas de controle de fluxo tão bem quanto o compilador?

Desculpe, não quero reclamar. Se eu for a única pessoa que se sente assim, não me importo de me afastar.

IMHO, este é um problema fundamental com interromper / continuar rotulado: é _muito_ expressivo, e a única maneira que conheço de atenuá-lo é recomendando o uso como "bom estilo" (seja lá o que isso signifique). Por exemplo, "use apenas quebra rotulada com valor do início ou final de um corpo de loop (não o meio)." Isso significaria que as formas possíveis de sair de um loop com um valor são fáceis de identificar e raciocinar.

É assim que evito a interrupção goto / rotulada em outros idiomas: https://gist.github.com/SoniEx2/fc5d3614614e4e3fe131/#file -special-lua-L4-L72

Isso é mais legível?

Nesse caso, talvez possamos descobrir algum tipo de sistema condicional baseado em blocos rotulados. Semelhante a isso , talvez.

O ponto de interromper e continuar sem rótulo é fornecer para os casos em que você não pode colocar facilmente a condição / valor no cabeçalho do loop. Alguns loops são simplesmente muito mais diretos, legíveis, rápidos, etc. com o intervalo no meio.

O ponto de interromper e continuar rotulados em loops é semelhante - às vezes, a única alternativa é introduzir uma nova variável, uma nova função (abusando assim de return ) ou alguma outra contorção que apenas torna as coisas mais difíceis de seguir, no entanto infelizmente, uma quebra rotulada pode ser complicada.

Mas esses dois recursos não são o objetivo deste tópico. Eles são bastante universais, precisamente por causa das melhorias que oferecem para expressar o fluxo de controle inerentemente complexo. Em vez disso, este tópico trata da quebra de um bloco não-loop. Isso é certamente mais novo, e as pessoas podem não saber como procurar por break fora de um loop, embora exigir um rótulo signifique que o sinal ainda estará lá, uma vez que você saiba o que significa.

Isso é o que eu quis dizer sobre o seu exemplo, @ mark-im- era um uso bastante padrão de um loop rotulado, em vez de um bloco rotulado.

Uma abordagem mais "baseada em princípios" seria usar a sintaxe return 'a value, que imediatamente continua a execução "após" o bloco' a, usando o valor value desse bloco.

Nota lateral sobre break vs return : Eu prefiro break porque é um fluxo de controle estático. return é dinâmico, pois volta para o chamador, que pode estar em qualquer lugar. Significa "Concluí minha responsabilidade, não há outro lugar onde olhar para ver o que faço". break sempre vai para algum lugar que tenha um escopo léxico, assim como acontece com os loops.

Eu acho que return 'label expr muito bem de uma perspectiva "faça o que eu digo" em que pode ser pensado como "expr de retorno ao rótulo do local" . Não acho que break 'label expr tão bem lido nesta perspectiva ...

Isoladamente de outras linguagens de programação, eu poderia ter defendido return 'label expr . No entanto, dado o fluxo de controle em outras linguagens, reutilizar return torna-se repentinamente uma opção muito menos viável e isso me inclina a favor de break 'label expr .

Estou firmemente convicto de que deveria ser break 'label expr e não return 'label expr . Fazer o contrário seria totalmente inconsistente com nosso uso existente de break 'label em loops.

@ SoniEx2 Acho que prefiro o snippet que você postou, principalmente porque as variáveis ​​são uma boa maneira de documentar invariantes de loop. OTOH, pode ser possível fazer o mesmo com os nomes dos rótulos (ou seja, sempre que este bloco rotulado é inserido, o P invariante se mantém). Eu acho que este é um lugar onde seria bom ter mais alguns exemplos de código da natureza ...

Proposta de estabilização

O recurso foi implementado em https://github.com/rust-lang/rust/pull/50045 por @ est31 e está disponível todas as noites desde 2016-05-18 (+17 semanas) e por isso está cozido o suficiente para estabilização. Além disso, não houve problemas relatados desde a fusão do PR que implementou isso. Todas as questões não resolvidas também foram resolvidas desde então, e há um forte consenso de que break deve ser a sintaxe de superfície em vez de return .

Assim, passo para estabilizar o valor de quebra de rótulo (RFC 2046).

@rfcbot merge

Relatório

  • Portão de recursos:

    • 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

  • Diagnóstico: https://github.com/rust-lang/rust/blob/master/src/librustc_passes/diagnostics.rs#L285
  • Testes:

    • 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 antes de FCP

@rfcbot concerne FIXME-in-tests

O último arquivo de teste atualmente tem um FIXME:

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

Isso deve ser resolvido antes de estabilizar.
Escrevi alguns testes para verificar se o comportamento esperado estava errado. o FIXME é realmente implementado:

// 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);
}

Testes semelhantes a esses devem ser adicionados antes de passarmos do FCP proposto para o FCP.

O membro da equipe @Centril propôs fundir isso. A próxima etapa é revisada pelo restante das equipes marcadas:

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

Preocupações:

  • custo-benefício (https://github.com/rust-lang/rust/issues/48594#issuecomment-422235234)
  • FIXME-in-tests (https://github.com/rust-lang/rust/issues/48594#issuecomment-421625182)
  • casos de uso (https://github.com/rust-lang/rust/issues/48594#issuecomment-422281176)

Assim que a maioria dos revisores aprovar (e nenhum objeção), isso entrará em seu período final para comentários. Se você identificar uma questão importante que não foi levantada em qualquer ponto deste processo, fale!

Consulte este documento para obter informações sobre quais comandos os membros da equipe marcados podem me dar.

@rfcbot concerne custo-benefício

Da proposta RFC FCP :

Outro grupo sentiu que o "custo-benefício" desse recurso para a linguagem não chegou ao fim. Em outras palavras, o aumento da complexidade que o recurso traria - dito de outra forma, a possibilidade de que as pessoas realmente o usem e você tenha que ler seu código, suponho, bem como o tamanho geral da linguagem - não era compatível com sua utilidade.

Ainda não acho que deveríamos ter esse recurso. Desde que foi implementado, tem sido muito usado? Nos casos em que foi usado, é significativamente pior usar funções? Se houver um benefício aqui, ele supera o custo de tornar a linguagem maior e mais complexa?

@nrc Eu compartilho a mesma preocupação. Eu entendo o argumento de tê-lo disponível para que as macros possam usá-lo, mas, ao mesmo tempo, prefiro não ter isso.

Eu não quero argumentos recauchutados do fio RFC original, mas eu acho que este é um ponto razoável para perguntar sobre experiências com esse recurso. Que usos ele viu?

analisadores escritos à mão, principalmente ... (eu gosto dos meus analisadores escritos à mão>. <)

seria mais útil / fácil de usar com propagação rotulada ( try_foo() 'bar? ).

@rfcbot concerne casos de uso

Resumindo algumas discussões sobre Discord: Gostaríamos de ver casos de uso concretos para isso, de código real, que não aparecem mais claramente quando reescritos para não usar este recurso.

~ FWIW, minha implementação de EXPR is PAT depende de break 'label value estar disponível pelo menos em AST, eu não sei como o desugaring poderia funcionar sem ele.
A implementação de EXPR? no compilador também depende de break 'label value . ~

~ É algum tipo de bloco de construção básico para recursos de fluxo de controle maiores, portanto, pode ser necessário implementá-lo no compilador de qualquer maneira.
Portanto, o "custo" aqui provavelmente é apenas disponibilizá-lo na sintaxe de superfície. ~

EDIT: Eu interpretei totalmente mal o problema, eu pensei que se tratasse de loop { ... break 'label value ... } , não de bloqueio { ... break 'label value ... } .
Nunca tive a oportunidade de experimentar este porque sempre esqueço que já está implementado.

@petrochenkov Conversando com @joshtriplett no Discord, eles apontaram que estavam preocupados com a complexidade do usuário, não com a implementação da linguagem.

Acho que o aumento da complexidade é mínimo: para os leitores, deve ser óbvio o que isso significa porque o conceito já existe em loops, etc.
Caso contrário, eu teria que usar um loop com uma instrução break incondicional, que é menos clara e, na verdade, há até mesmo um lint cortante (never_loop) sobre isso. Então eu acho que há um benefício.

Quanto aos casos de uso, esse tópico já apareceu na RFC. Eu indiquei meu caso de uso aqui . Veja também o caso de uso listado por @scottmcm diretamente abaixo. Talvez haja mais no tópico, idk. @joshtriplett isso resolve a questão do caso de uso?

Concordo com @nrc e @joshtriplett e também quero levantar uma questão de processo aqui: aceitamos provisoriamente este RFC com uma advertência explícita de que a estabilização foi bloqueada ao revisitar as questões que @nrc e @joshtriplett levantaram, mas @Centril 's A proposta de mesclagem não menciona essa preocupação de bloqueio, e trata isso como uma mesclagem muito padrão do tipo "recurso criado". Não estou culpando o @Centril por isso, mas um colapso do processo: se vamos aceitar recursos provisoriamente com bloqueadores não resolvidos na estabilização, precisamos rastrear esses bloqueadores.

Foi preocupante para mim, em termos de todos os nossos processos, ver que passavam 2 dias e meio sem a preocupação de bloqueio ser mencionada e com a maioria dos membros da equipe já tendo verificado sua caixa. É concebível, uma vez que não exigimos mais consenso ativo de todos os membros, que isso poderia ter entrado no FCP sem que o bloqueador tivesse sido aumentado. Isso parece uma subversão do acordo anterior que me levou a concordar com a fusão da RFC, e acho que é inteiramente causado por rastreamento insuficiente de informações.

@semoutboats Sim, exatamente. Isso me torna um pouco menos inclinado, no futuro, a aceitar as coisas em uma base "vamos XYZ durante o processo de estabilização", até que tenhamos algum processo em vigor que torne extremamente improvável que seja perdido.

@withoutboats

Não estou culpando o @Centril por isso, mas um colapso do processo: se vamos aceitar recursos provisoriamente com bloqueadores não resolvidos na estabilização, precisamos rastrear esses bloqueadores.

Mesmo assim, desculpas; Eu não sabia da advertência, mas deveria ter verificado essas coisas, mesmo assim, quando criei o problema de rastreamento.

Isso parece uma subversão do acordo anterior que me levou a concordar com a fusão da RFC, e acho que é inteiramente causado por rastreamento insuficiente de informações.

Eu deveria ter feito uma pergunta não resolvida para isso; Eu acredito que o erro do processo aconteceu naquele ponto.
Quanto a como melhorar o processo; Acho importante que as questões não resolvidas cheguem ao


Quanto à proposta fcp-merge; Eu pessoalmente acho que seria útil por razões de uniformidade e para uso por macros. No entanto, se você acredita que é muito cedo para propor estabilização; sinta-se à vontade para cancelar a proposta :)

@ est31

Caso contrário, eu teria que usar um loop com uma instrução de interrupção incondicional,

Ou reestruture o código para evitar qualquer um deles.

Eu indiquei meu caso de uso aqui .

Por que não substituir break 'pseudo_return por return Ok(vectors); ?

como mencionei aqui , isso é útil para analisadores escritos à mão (mesmo sem propagação rotulada ( try_foo() 'bar? )).

label-break-value permite a fácil imperativização de código que de outra forma seria funcional. geralmente, o código imperativo tende a ser mais legível do que o código funcional.

Ou reestruture o código para evitar qualquer um deles.

Claro, Rust está se tornando completo. Mas a reestruturação pode ser um pouco difícil. No geral, você pode rejeitar quase todos os recursos de açúcar (e este é o recurso de açúcar) com base em "você pode simplesmente usar as formas existentes".

Por que não substituir a quebra 'pseudo_return por return Ok (vetores) ;?

Na verdade, neste caso você está certo, pode-se substituir a pausa por um retorno Ok. Mas, em outros casos, você pode querer fazer o processamento depois, etc. O verificador de empréstimo funciona mal entre os limites da função, você não pode fatorar cada um desses blocos em uma função.

De qualquer forma, quebrei meu silêncio sobre comentar sobre recursos da linguagem por meios oficiais, e tbh estou me arrependendo. Todos os mesmos pontos, refeitos indefinidamente. Essa merda é uma perda de tempo, desculpe. Portanto, não espere mais comentários de mim.

@ est31 Agradeço muito por você fornecer os detalhes; obrigado.

Há um problema de acessibilidade para testar e usar essas coisas, devido ao requisito de Rust noturno.

Meu objetivo é estável. Espero que isso possa ser resolvido algum dia.

Se quisermos casos de uso, aqui está um que achei há um tempo; basicamente uma elaboração do "fazer processamento depois" de @est31 : escrevi um lexer que lida com prefixos literais de string C ++ (no caso real do C ++, uma explosão combinatória de { L , u8 , u , U ,} { R ,}) e tokens multibyte que com "lacunas" no número de bytes usados ​​(não tenho certeza de que faz sentido sem o exemplo). A função get_token atualmente se parece com isto:

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),
        },

        // ...
    }
}

Observe as pirâmides de _ => self.identifier(rest) s (repetidas quatro vezes para u , U , R e L ) e _ => self.make_token(Dot, rest) s, formando uma espécie de estilo de passagem de continuação onde identifier , string_literal , etc. todos devem também chamar make_token .

Eu teria gostado de consolidar as coisas de volta a um estilo menos continuado usando break -from-block, e quase fiz isso via loop s, mas considerei essa versão muito estranha de ler. Para ser mais específico:

  • Eu teria movido todas as chamadas make_token para um único local após o match decode_byte(self.source) e o embutido - é pequeno e contém unsafe com seus invariantes sustentados por get_token .
  • Eu teria usado break 'label self.string_literal(..) para entrar em curto-circuito ao encontrar um " ou ' e, em seguida, combinado todas as self.identifier(..) chamadas ao final desse braço de correspondência .

    • Também posso ter sido capaz de linearizar a explosão combinatória de prefixos - verifique se há u / u8 / U / L , em seguida , verifique se há R . Isso usa menos break 'label s, mas ainda um punhado.

  • Eu teria usado break 'label (Ellipsis, rest) para entrar em curto-circuito uma vez encontrando ... , e então combinado (Dot, rest) s no final daquele braço de partida.

No geral, isso é basicamente o "fluxo de controle nivelado com if + retorno antecipado", sem a necessidade de extrair coisas em uma função separada. Isso é extremamente valioso neste caso por alguns motivos:

  • A maioria dessas funções seriam minúsculas, funções de uso único e sem um bom nome, servindo apenas para tornar as coisas ainda menos legíveis do que este estilo de continuação.
  • Algumas dessas funções exigiriam vários parâmetros extras que, de outra forma, seriam apenas locais diretos com tipos inferidos.

    • Ou, alternativamente, fechamentos, que em alguns desses casos causariam problemas de verificador de empréstimo.

  • Como mencionei acima, existem invariantes de segurança sendo mantidos em todas as funções aqui. Quanto mais código de máquina de estado em linha reta aqui, melhor, especialmente quando as pessoas voltarem mais tarde para fazer pequenas alterações em novos tipos de tokens.

Eu escreveria tudo, mas acho que nunca cometi essa tentativa (provavelmente porque me deparei com todos os problemas que listei acima) e já gastei palavras suficientes aqui. :)

@SergioBenitez você poderia http://rocket.rs/ de label_break_value e sua opinião sobre a estabilização?

@Centril Claro! Aqui está a essência:

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)
}

Usando esse recurso, evito:

  • Dividir o bloco em uma função diferente para "retornos" iniciais.
  • Adicionando Transform::Borrowed a cada valor de "retorno" no bloco.
  • Possível incorreção se um Transform diferente for retornado em casos diferentes.

    • Nota: Este é um invariante particular para Rocket e este pedaço de código.

Fiquei muito feliz em ver que isso existia. Isso é exatamente o que quero escrever. Dito isso, posso escrever de forma diferente para não depender desse recurso.

@SergioBenitez Obrigado! Eu me pergunto se você poderia (eventualmente) reescrever isso com try { .. } ? Igual a:

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 Sim, isso funcionaria, desde que haja apenas um caminho de sucesso, que é o caso aqui.

Ou reestruture o código para evitar qualquer um deles.

Ao fazer isso, você corre o risco de adicionar ramificações adicionais ou chamadas de sub-rotina que não podem ser resolvidas pelo otimizador. Para mim, essa transformação de fluxo de controle parece ser uma tarefa bastante complicada.

Nos casos em que foi usado, é significativamente pior usar funções? Se houver um benefício aqui, ele supera o custo de tornar a linguagem maior e mais complexa?

Uma pequena função pode ser embutida, mas, mesmo assim, você ganharia um inchaço de código desnecessário ou, digamos, duplicações de código idiotas.

Eu preferiria o código binário mais elegante possível. Você pode fazer isso por meio de um fluxo de controle um pouco mais avançado, quase o mesmo que um retorno antecipado de uma função.

Eu me pergunto se você poderia (eventualmente) reescrever isso com try {..}? Igual a:
[...]

Isso parece um pouco confuso, conforme os branches são introduzidos, apenas para serem otimizados. Mas em algumas situações, pode-se precisar de ambos. Assim, tendo

'a: {try{...}}

ou

'a: try {...}

seria bom.

Gostaríamos de ver casos de uso concretos para isso, de código real, que não aparecem mais claramente quando reescritos para não usar esse recurso.

Por favor, sinta-se à vontade para me desonrar:

Pode não ser o melhor código, mas eu queria que as coisas funcionassem.

Eu gostaria de reafirmar experimentalmente o loop principal como uma máquina de estado em termos de estilo de passagem de continuação. Mas continuações e chamadas finais impostas são outro tópico.

Seria muito bom ver esse recurso estabilizado. Tenho vários casos de uso em que essa sintaxe simplifica e esclarece a intenção de maneira significativa. Para longas seções envolvendo a execução de operações falíveis e o desempacotamento de seus resultados, essa sintaxe é excelente.

Eu concordo com @zesterer que a estabilização desse recurso seria um benefício para o ecossistema e tornaria certos padrões menos incômodos de escrever: +1:

FWIW, recentemente usei-o no compilador pela primeira vez.
O padrão é o mesmo dos exemplos anteriores - estamos verificando várias condições e paramos de fazer o que estamos fazendo se alguma delas for verdadeira.
https://github.com/rust-lang/rust/blob/21f26849506c141a6760532ca5bdfd8345247fdb/src/librustc_resolve/macros.rs#L955 -L987

@erickt também escreveu um código que queria usar isso pelo mesmo motivo: verificar várias condições, interromper assim que uma delas se tornar falsa. Você pode fingir isso com fechamentos imediatamente chamados ou let _: Option<()> = try { ... None?; .. }; mas ambos são muito hacky.

@withoutboats @nrc @joshtriplett Você tem alguma opinião sobre os casos de uso apresentados até agora por várias pessoas?

Ping @withoutboats @nrc @joshtriplett :) - na última reunião discutimos a possibilidade de que vocês tentassem reescrever alguns dos exemplos dados acima e nos mostrar como você os estruturaria.

Eu me senti estimulado pela postagem do blog de greydon a escrever um comentário aqui sobre por que não quero estabilizar esse recurso. Acho que foi um pouco injusto da minha parte ter concordado com esse experimento; é difícil provar uma negativa, mas não tenho nenhuma ideia de que tipo de amostra de código poderia superar minha oposição a adicionar essa forma de fluxo de controle à linguagem.

Minha objeção geral é que simplesmente acho que o Rust não precisa de mais construções de fluxo de controle de ramificação aberta. Temos correspondência e loop e, em seguida, no topo desses tipos de dados algébricos, sintaxe de açúcar, abstração de função e macros para criar uma grande variedade de padrões de fluxo de controle para qualquer usuário comum manipular. Isso já parece opressor e eu pessoalmente tive que adotar algumas regras para gerenciar a complexidade da árvore de decisão (por exemplo, eu tenho uma regra para nunca alcançar combinadores como uma primeira passagem: somente se for óbvio após o fato, seria mais agradável como combinadores).

Eu gostaria de evitar a adição de opções mais amplamente aplicáveis ​​e altamente flexíveis à caixa de ferramentas de fluxo de controle de Rust. Estou interessado apenas em adicionar fluxo de controle voltado para casos de uso importantes e específicos e que tenha um alto impacto ergonômico. Essa construção, em minha opinião, tem os atributos exatamente invertidos: é amplamente aplicável, extremamente maleável e apenas ligeiramente mais conveniente do que as alternativas.

Além disso, acho que esse recurso tem outra qualidade muito negativa: irregularidade devido à compatibilidade com versões anteriores. É muito irregular para break quebrar os blocos apenas se eles estiverem rotulados, quando sai dos loops não rotulados. Isso torna o recurso mais difícil de entender do que seria se fosse regular, exacerbando os atributos negativos exatamente onde acho que são os piores - a compreensibilidade.

Também acho que há muitos recursos muito mais importantes nos quais nos concentrarmos e, uma vez que temos uma divisão clara sobre isso, prefiro adiar qualquer consideração a respeito, em vez de tentar passar por um longo processo de consenso sobre essa proposta.

Eu acho que a coisa certa a fazer é encontrar cada caixa que usa esse recurso e reescrever o código para não usar esse recurso.

@withoutboats Obrigado por articular de maneira muito eficaz muitas das mesmas objeções que eu também tenho.

Vou prosseguir e ser ousado aqui:

@rfcbot cancel
@rfcbot postpone

Proposta @joshtriplett cancelada.

O membro da equipe @joshtriplett propôs adiar isso. A próxima etapa é revisada pelo restante das equipes marcadas:

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

Nenhuma preocupação listada atualmente.

Assim que a maioria dos revisores aprovar (e nenhum objeção), isso entrará em seu período final para comentários. Se você identificar uma questão importante que não foi levantada em qualquer ponto deste processo, fale!

Consulte este documento para obter informações sobre quais comandos os membros da equipe marcados podem me dar.

@withoutboats

Eu gostaria de evitar a adição de opções mais amplamente aplicáveis ​​e altamente flexíveis à caixa de ferramentas de fluxo de controle de Rust. Estou interessado apenas em adicionar fluxo de controle voltado para casos de uso importantes e específicos e que tenha um alto impacto ergonômico.

Eu concordo com esse princípio e acho que esse recurso atende a essa barreira (fluxo de controle familiar direcionado a casos de uso importantes e específicos). Várias vezes, quando revisei ou escrevi código como os exemplos fornecidos acima, sinto que essa é de longe a maneira mais limpa e fácil de ler de escrever esse código. Reutilizar construções de fluxo de controle existentes como loop com um break incondicional no final engana o usuário, e as funções aplicadas imediatamente são frequentemente insuficientes devido ao uso de ? , await! ou outras construções de fluxo de controle intermediárias.

Usar loop + trailing break é confuso, e devemos preferir que os usuários declarem sua real intenção em vez de exigir que abusem de uma ferramenta feita para um estilo diferente de fluxo de controle. Isto é semelhante ao fato de que temos até um loop construto, enquanto outros idiomas estão contentes com while true { ... } . Isso torna possível escrever um código que expressa uma intenção mais clara e, como resultado, é mais legível.

Além disso, esse recurso é algo que eu sempre esperei que Rust tivesse, visto que rotulamos a maioria das outras coisas e quebramos das coisas rotuladas, o fato de que você não pode rotular ou quebrar um bloco parece confuso e errado para mim.

TL; DR: Eu acho que este recurso está dando suporte a casos de uso do mundo real que só podem ser escritos de outra forma por meio de instruções if fortemente aninhadas ou abusando de outras construções como valor de quebra de loop. É um pequeno acréscimo à sintaxe de superfície que faz com que os blocos se comportem como eu esperava que se comportassem e me permite escrever o código que quero dizer, em vez de algo muito mais hackeado.

@cramertj Desculpe, estou um pouco confuso. Parece que você e @withoutboats / @joshtriplett estão dizendo exatamente o oposto um do outro?

(fwiw, concordo com @withoutboats / @ joshtriplett)

@ mark-im Não concordo com eles que este recurso deva ser fechado. Acho que deve ser mesclado (conforme indicado por meus comentários e caixa de seleção marcada acima).

Concordo com @withoutboats que não devemos adicionar novas ferramentas desconhecidas que não sejam motivadas por casos de uso específicos e importantes. Acho que esse recurso parecerá familiar e é motivado por casos de uso específicos e importantes.

@withoutboats

Acho que foi um pouco injusto da minha parte ter concordado com esse experimento; é difícil provar uma negativa, mas não tenho nenhuma ideia de que tipo de amostra de código poderia superar minha oposição a adicionar essa forma de fluxo de controle à linguagem.

Eu não acho que a barra que definimos foi "mostre-nos que nenhum código poderia existir que pudesse ser melhor escrito com o valor de quebra de rótulo" - é "mostre-nos que o código específico para o qual queremos o valor de quebra de rótulo poderia ser escrito mais claramente de outra maneira. " Esta conversa começou quando você e outras pessoas pediram exemplos motivadores de onde esse recurso é útil, e muitas pessoas neste tópico (incluindo eu) forneceram exemplos. Eles não foram convincentes para você ou @joshtriplett , então agora estou perguntando como vocês dois escreveriam esses exemplos sem o valor de quebra de rótulo. Você concorda que os exemplos são melhor escritos usando o valor de quebra de rótulo? Em caso afirmativo, sua posição é que eles não são comuns o suficiente para compensar o custo de permitir que os usuários escrevam códigos de quebra a bloco potencialmente complicados?

Vou prosseguir e ser ousado aqui:

@rfcbot cancel

Proposta @scottmcm cancelada.

É muito irregular a quebra de blocos apenas se eles estiverem rotulados, quando ele quebra de loops não rotulados.

Não tenho certeza se foi mencionado no tópico ou não, mas os blocos sendo não segmentáveis ​​por break; tornam os blocos rotulados qualitativamente mais poderosos do que os rotulados loop s em algum sentido.

Com blocos rotulados você pode fazer uma macro de fluxo de controle que suporte break; s dentro dela, com a macro de infra sendo completamente invisível para o usuário.
Com loops rotulados, há sempre o risco de que break; rótulo seja usado e seja capturado pela macro infra ao invés do alvo pretendido.

No meu protótipo EXPR is PAT , usei os loop s usuais para desugar inicialmente, mas as coisas quebraram devido ao problema mencionado anteriormente, não consegui inicializar o compilador em particular.
Blocos rotulados não foram implementados naquela época, então eu tive que introduzir um especial " loop " não direcionado no AST e usá-lo na desauguração.

Conforme indicado pela minha caixa de seleção, sou a favor de estabilizar isso agora. Eu acho que esta é uma extensão natural da linguagem que facilita macros, bem como o fluxo de controle que não se ajusta facilmente aos padrões funcionais. Mesmo que o NLL torne as vidas não lexicais, acho que ser capaz de anotar blocos com as vidas também me parece pedagogicamente útil, assim como a atribuição de tipos.

No entanto, como o consenso tem sido difícil de alcançar, no interesse de encontrá-lo, sugiro que tentemos acelerar o trabalho em try { ... } , além de acelerar o trabalho de experimentos com https://github.com / rust-lang / rust / issues / 53667 (ou simultaneamente com a sintaxe EXPR is PAT @petrochenkov).

Desculpe, qual é o status disso agora? Estamos em FCP ou não?

@ mark-im Visto que nenhum dos rótulos proposed-final-comment-period ou final-comment-period está relacionado com o problema, não estamos no FCP ou uma proposta para um.

(e nunca estivemos no FCP, embora tenha havido uma proposta inicial para entrar no FCP com todas as caixas, exceto três marcadas)

@scottmcm

Vou prosseguir e ser ousado aqui:

Esta repetição das palavras de

Estou convencido de que esta é, de fato, uma boa ideia. Eu fui muito contra esse recurso anteriormente e ainda não acho que seja um recurso que deva ser usado por programadores. Os casos de uso acima são um tanto persuasivos, mas acho que ainda podem ser melhor fatorados em funções menores. O caso que achei realmente convincente é o da saída de macros. Se o usuário puder inserir return alguma forma, isso impede a tradução em funções. No entanto, o que realmente queremos é simplesmente goto . Eu me pergunto se a 'grande' solução são macros que podem gerar HIR, em vez de tokens, mas isso é um pouco estranho. Nesse ínterim, seria bom ter esse recurso para autores de macro, então, no geral, acho que devemos estabilizar.

Alguém já tentou reescrever as coisas de uma forma mais lenta, menos intuitiva e menos ergonômica?

Quebrar blocos é como ir para ergonômico. Praticamente nenhum dos problemas de goto, mas é igualmente poderoso.

Com base na mudança de opinião de label_break_value .
Meu relatório pode ser encontrado em https://github.com/rust-lang/rust/issues/48594#issuecomment -421625182.

@rfcbot merge
@rfcbot concerne FIXME-in-tests

Para garantir que @joshtriplett e @withoutboats tenham tempo para levantar quaisquer preocupações que ainda possam ter, irei registrar tal preocupação. Vou levantá-lo assim que me disserem que não têm esse tipo de preocupação ou quando um deles tiver levantado sua própria preocupação.

@rfcbot preocupação dar-barcos-e-josh-tempo-para-levantar-quaisquer-preocupações-que-eles-ainda-possam-ter

Como uma nota de processo para este problema e para todos os outros, evite cancelar propostas para frente e para trás ... se você acha que algo não deve avançar, apenas use as preocupações para isso.

O membro da equipe @Centril propôs fundir isso. A próxima etapa é a revisão pelo restante dos membros da equipe marcados:

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

Preocupações:

Assim que a maioria dos revisores aprovar (e no máximo 2 aprovações pendentes), isso entrará em seu período final para comentários. Se você identificar uma questão importante que não foi levantada em qualquer ponto deste processo, fale!

Consulte este documento para obter informações sobre quais comandos os membros da equipe marcados podem me dar.

@rfcbot preocupa o bloqueio

Já expressei minha posição, que é que Rust não deveria ter esse recurso. Duvido que seja uma prioridade alta o suficiente para o projeto para que eu possa dedicar tempo para me envolver em um processo de obtenção de consenso sobre esta proposta em qualquer momento no futuro próximo.

@rfcbot resolve dar-barcos-e-josh-tempo-para-levantar-quaisquer-preocupações-que-eles-ainda-possam-ter

Se for importante: o Zig também possui esse recurso .

Em 5 de janeiro de 2019 9:18:29 PST, Mazdak Farrokhzad [email protected] escreveu:

@rfcbot resolve
dar-barcos-e-josh-tempo-para-levantar-quaisquer-preocupações-que-eles-ainda-possam-ter

Já levantamos preocupações e elas equivalem a "isso não deveria estar na linguagem". Essas preocupações não vão embora.

Eu prefiro a noção do nrc de que as macros devem ser capazes de gerar IR. Isso parece uma solução completamente razoável para esta e muitas possibilidades futuras, sem necessariamente adicionar à sintaxe superficial da linguagem.

@joshtriplett

Já levantamos preocupações e elas equivalem a "isso não deveria estar na linguagem". Essas preocupações não vão embora.

Para ser claro, eu quis dizer registrá-los com @rfcbot porque o bot não verá preocupações antigas registradas depois que uma proposta foi cancelada. Levantei essa preocupação porque sei que você tem preocupações, como uma cortesia para com você.

Eu prefiro a noção do nrc de que as macros devem ser capazes de gerar IR. Isso parece uma solução completamente razoável para esta e muitas possibilidades futuras, sem necessariamente adicionar à sintaxe superficial da linguagem.

Não tenho ideia de como seria ou de que é uma ideia melhor; parece bastante especulativo (no sentido de "isso pode levar anos antes que um projeto seja aceito") e mais caro do que a solução de baixo custo de break 'label expr que também tem benefícios além de ser o resultado de macro expansão.

_Como uma nota de processo para este problema e para todos os outros, evite cancelar propostas de um lado para outro ... se você acha que algo não deve avançar, apenas use as preocupações para isso._

Eu certamente concordo com isso. No entanto, não acho que uma preocupação seja o mecanismo apropriado aqui. Uma preocupação parece "se isso fosse resolvido, eu aprovaria". Aqui, o problema é "isso não deve avançar de forma alguma, 'mesclar' é o destino errado, eu gostaria de 'fechar' em vez disso". Isso não parece melhor tratado pelo mecanismo de uma "preocupação".

@joshtriplett Como ponto de interesse, você poderia me indicar as preocupações que você mencionou anteriormente? Eu examinei o tópico RFC original e não tenho certeza do que está sendo especificamente referido. Obrigado.

@rfcbot diz respeito à ergonomia e otimização / desempenho (exemplo sem ferrugem de desempenho e ergonomia reduzidos https://gist.github.com/SoniEx2/fc5d3614614e4e3fe131#file-special-lua)

idk se estou usando isso direito

(observe como parte do "desugaring" (na verdade, eu diria que é exatamente o oposto de desugaring - goto é um primitivo, faz um loop de desugar em goto) é bastante horrível)

(note também que lua não tem nada rotulado, então ele força você a usar goto. mesmo assim, acredito que pelo menos um break-block ainda seria necessário, mas provavelmente com um "desugaring" mais limpo.)

@rfcbot concern should-close-not-merge

(Como sugerido por @centril no Discord.)

Não tenho certeza se este é um argumento a favor ou contra a estabilização, mas observe que isso é trivialmente fácil de codificar no Rust estável hoje:

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

Por um lado, devemos apenas estabilizar isso porque mal está crescendo a linguagem, apenas elidindo for _ in 0..1 , por outro lado, não devemos estabilizar isso porque há uma maneira fácil de fazer isso hoje (para quando é realmente necessário) e não devemos encorajar o uso de tal antipadrão.

parece meio confuso e o rótulo é desnecessário.

quebrar blocos é muito menos confuso e requer o rótulo.

Não entendo por que você acha que quebrar blocos é um antipadrão. o que se aplica a X não se aplica necessariamente a não exatamente X. Posso comprar um computador por US $ 299,99, mas não por US $ 299,98, mesmo que eles sejam "basicamente" iguais.

@nrc

Não tenho certeza se este é um argumento a favor ou contra a estabilização, mas observe que isso é trivialmente fácil de codificar no Rust estável hoje:

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

Portanto, por um lado, devemos apenas estabilizar isso porque a linguagem mal está crescendo, apenas eliminando for _ in 0..1 ,

'a: for _ in 0..1 { break 'a } pode funcionar se o objetivo explícito é que ninguém jamais escreva 'a: { ... break 'a e; ... } ; no entanto, tem a desvantagem de gerar IR de lixo que o verificador de tipo, MIR e LLVM devem processar, piorando os tempos de compilação (pelo menos em comparação com LBV).

por outro lado, não devemos estabilizar isso porque há uma maneira fácil de fazer isso hoje (para quando for realmente necessário) e não devemos encorajar o uso de tal antipadrão.

Acho que discordamos que o LBV seja um antipadrão. No final do dia, optamos por imbuir Rust com fluxo de controle imperativo, em vez de apenas ter Rust como uma linguagem de programação funcional. Embora eu pudesse ter preferido não ter esse fluxo de controle, é um fato consumado. A questão é então se LBV é de alguma forma mais ilegível ou problemático do que outros mecanismos imperativos em Rust. Eu não acho que seja.

Embora eu ache que as funções devem ser mantidas curtas (verticalmente) e rasas (recuo), é melhor ser longo e raso do que longo e profundo. Acho que o LBV ajuda a evitar profundidade em alguns dos fluxos mais complicados. LBV também parece legível para mim, pois denota explicitamente para onde irá, tornando o fluxo bem compreendido. Resumindo, considero o LBV um dos fluxos de controle imperativos menos problemáticos.

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

eles podem parecer semelhantes, mas não são exatamente iguais. enquanto o primeiro é indiscutivelmente um antipadrão, o segundo é indiscutivelmente não.

meio como loop {} vs while true {} (concedido loop pode retornar um valor, enquanto while não pode, mas vamos ignorar isso um pouco)

@nrc Isso não tem exatamente o mesmo comportamento: https://github.com/rust-lang/rust/issues/48594#issuecomment -450246249

Em particular, o comportamento de break; e continue; é diferente. Considere este código hipotético:

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

Se some_macro expandir para 'a { ... } , isso terá um comportamento diferente do que se expandir para 'a: for _ 0..1 { ... }

@ SoniEx2

Alguém já tentou reescrever as coisas de uma forma mais lenta, menos intuitiva e menos ergonômica?

Por favor, tente se comunicar suas preocupações de uma forma mais positiva e produtiva. Zombar dos outros não nos leva a lugar nenhum e faz as pessoas se sentirem mal. Estamos todos aqui porque queremos ter certeza de que Rust é a melhor linguagem de programação que pode ser, e parte desse processo é garantir que a comunidade seja o mais positiva e encorajadora possível.

Talvez eu esteja tendo problemas com algum grande mal-entendido. Não vou fingir ser um especialista em LLVM, componentes internos de Rust ou coisas semelhantes. Dito isso, tenho alguma experiência rudimentar com design de compiladores e estou confuso sobre qual é a preocupação. Se alguém pudesse explicar as coisas, eu realmente apreciaria.

A maneira como vejo as coisas:

1) Isso não altera a ergonomia do controle de fluxo. Já existem construções como esta no Rust, como 'a: loop { if x { break 'a; } break; } .

2) Isso não muda particularmente a ergonomia dos retornos de bloco: os loops já são capazes de retornar valores.

Para mim, isso parece mais a conclusão intuitiva de um conjunto de recursos que Rust já possui - generalizando esses recursos para todos (ou pelo menos mais) blocos.

Em termos de preocupações sobre isso ser muito semelhante a goto , estou ainda mais confuso. Isso não adiciona nada que já não seja possível no Rust e não permite saltos para trás (o principal problema que resulta no uso incorreto de goto regredindo em código espaguete), então estou falhando para entender quais ramificações esse recurso pode ter no codegen, visto que isso parece ser apenas uma adição de sintaxe para um loop sempre interrompido.

Alguém poderia me explicar em termos mais específicos quais problemas existem?

Em termos de procedimento, IMO não é apropriado registrar preocupações no estilo concern blocking ou concern should-close-not-merge : esta preocupação não lista um problema com o recurso proposto ou qualquer coisa que possamos trabalhar para resolver- - é apenas um bloqueador permanente. Não acho que o mecanismo de preocupação do rfcbot deva ser usado para bloquear permanentemente os recursos, mas apenas para ajudar a rastrear a resolução das preocupações e garantir que sejam tratadas / resolvidas de forma adequada. Meu entendimento do processo era que a intenção era usar uma caixa de seleção desmarcada para marcar sua discordância e que as preocupações deviam ser usadas para arquivar questões específicas e discutíveis sobre o recurso e sua funcionalidade.

Eu poderia ter preenchido questões semelhantes do tipo "Não gosto disso" em outros recursos dos quais discordei pessoalmente (ver, por exemplo, uniform_paths), mas não acredito que obstrução infinita seja uma maneira produtiva de fazer design de linguagem.

Se houver preocupações específicas sobre a maneira como esse recurso interage com outras partes da linguagem que devem ser discutidas / resolvidas (por exemplo, "isso parece que pode ser evitado por try { ... } " ou "ajuda a permitir que funções grandes do idioma ") Acho que seria mais produtivo arquivá-los dessa forma.

@cramertj Eu não teria apresentado tal reclamação nesse formulário se @Centril não tivesse sugerido explicitamente. (Eu pessoalmente teria preferido se o FCP não tivesse sido proposto.)

O que você sugeriria como o processo apropriado para "isto deve ser fechado", se alguém entrou com um P-FCP com o rfcbot por algo diferente de "fechar"? "certifique-se de que eles sejam tratados / resolvidos de forma adequada" soa equivalente a "isso vai se estabilizar um dia, o que é preciso fazer para chegar lá?". Isso não deixa um caminho no fluxo para "isso nunca deve ser estabilizado, isso não deve fazer parte da linguagem".

Meu entendimento do processo era que a intenção era usar uma caixa de seleção desmarcada para marcar sua discordância

Então, precisaríamos mudar o processo de volta para exigir que todas as caixas de seleção marcadas para continuar.

Não acredito que a obstrução infinita seja uma forma produtiva de fazer design de linguagem.

Eu também não acredito. Gostaria de encontrar um caminho para concluir isso também, só gostaria de ver concluído em uma direção diferente.

Para registro, com base nas experiências com este RFC, não acho que seja uma boa ideia responder novamente a um RFC com uma advertência de "podemos avaliar / resolver durante a estabilização se devemos prosseguir", porque me parece claro que fazer isso produz problemas processuais críticos como este.

Precisamos de um caminho para dizer "sim, posso ver como esse recurso individual tornaria a linguagem mais expressiva, mas com base na avaliação geral da linguagem, não tem seu peso e não quero expandir ainda mais o superfície da língua desta forma ". Acredito que precisamos fazer essa chamada regularmente, para que cada recurso proposto não seja visto como inevitável de alguma forma e apenas uma questão de reprimir objeções e persistir.

Vou repetir o que disse antes porque parece que não foi possível: https://github.com/rust-lang/rust/issues/48594#issuecomment -451795597

Eu basicamente falei sobre as diferenças entre usar um loop e usar um bloco de quebra.

principalmente, os blocos de quebra têm sintaxe diferente, semântica (ligeiramente) diferente (no mínimo com respeito à quebra não rotulada), eles significam intenções diferentes e algumas outras coisas menores.

o rfcbot oferece suporte a anticoncepcionais?

@joshtriplett

Precisamos de um caminho para dizer "sim, posso ver como esse recurso individual tornaria a linguagem mais expressiva, mas com base na avaliação geral da linguagem, não tem seu peso e não quero expandir ainda mais o superfície da língua desta forma ". Acredito que precisamos fazer essa chamada regularmente, para que cada recurso proposto não seja visto como inevitável de alguma forma e apenas uma questão de reprimir objeções e persistir.

Sim, é um infeliz risco de nosso processo atual aceitar ou rejeitar um recurso que exija total consenso da equipe apropriada. Quando a equipe de idiomas é tão grande quanto agora, é difícil sempre conseguir isso - pensei, pelos comentários acima, que faltavam apenas exemplos de código para motivar por que esse recurso é importante, mas agora parece que você concorda que há é um código que pode ser melhor escrito usando esse recurso, mas não estamos convencidos de que valha a pena os custos que você vê aqui. Não tenho certeza de uma maneira de convencê-lo de que esses casos são motivação suficiente, pois parece que continuar a fornecer exemplos (incluindo o exemplo macro, que é literalmente impossível de escrever em outro estilo) não é suficiente.

Da mesma forma, estou bastante confiante de que, pessoalmente, continuarei convencido de que esse recurso deve ser mesclado: IMO, ele não apenas mostra sua importância por meio de uma variedade de exemplos em que é a melhor / única opção, mas a linguagem é realmente mais simples com sua adição (já que não permitir blocos rotulados é surpreendente para mim, dado que permitimos outras construções rotuladas).

Se você concordar com meu resumo acima de sua posição, então parece que estamos em um impasse infeliz (e, acredito, histórico!), Sem um processo. Uma opção é interpretar as regras atuais do rfcbot como fiz acima para significar "nenhuma caixa de seleção sinaliza sua discordância, três membros são necessários para anular a maioria", mas eu não acho que era assim que as regras eram quando foram introduzidas, então Acho que foi insincero de minha parte sugerir que você deveria seguir esse procedimento (embora eu mesmo o tenha feito em outro lugar).

Eu já ouvi sugestões de que poderíamos introduzir um limite de tempo para recursos indo de aprovado-> implementado-> estabilizado, e que deveríamos "fechar automaticamente" os recursos que ficam para trás em um esforço para evitar um acúmulo cada vez maior. Algo como isso poderia resolver este caso, onde eu (e, eu acho, vários outros na equipe) não marcarei as caixas de seleção para fechar, nem você marcará uma caixa de seleção para aceitar (ainda agora tenho medo de dizer isso que eu ' estou jogando fora um último esforço para convencê-lo!: sorria :).

Preocupo-me com o fato de que, com equipes cada vez maiores, perderemos a capacidade de chegar a um consenso sobre os recursos e que será difícil orientar a linguagem de maneira coerente, especialmente na equipe de design de idioma. Os limites de tamanho da equipe parecem uma solução óbvia para isso - é possível receber informações de um grande número de pessoas, mas quase impossível construir um consenso absoluto entre um grupo suficientemente grande. Outros provavelmente argumentarão que recursos controversos não devem ser combinados em um esforço para proteger a linguagem de recursos incorretos. Pessoalmente, acho que é improvável que a comunidade nos deixe cometer muitos desses erros sem um aviso adequado, mas é uma ideia.

Vou tentar iniciar um tópico separado para discutir a evolução do processo aqui e, enquanto isso, peço que as pessoas que participam desse tópico postem apenas se houver novos casos de uso críticos a serem considerados que sejam notavelmente diferentes dos aqueles acima, ou se há uma razão significativa não considerada pela qual esse recurso não deve ser adicionado ao idioma. Ler toda a história nesses megategmentos é difícil, mas se torna ainda mais difícil quando as postagens são repetidas continuamente ou quando o tópico é preenchido com comentários inúteis. (ele diz que agora digitou um dos comentários mais longos de todo o tópico XD)

TL; DR: Acho que estamos travados, devemos ter um processo para isso-- Vou começar essa conversa em outro lugar e vinculá-la aqui. Caso contrário, não comente, a menos que você tenha novas informações significativas que precisem ser consideradas.

@cramertj

é um infeliz risco de nosso processo atual aceitar ou rejeitar um recurso que exija total consenso da equipe apropriada

Eu honestamente consideraria isso um recurso.

Pensei, pelos comentários acima, que faltavam apenas exemplos de código para motivar por que esse recurso é importante, mas agora parece que você concorda que existe um código que pode ser melhor escrito usando esse recurso, mas não está convencido de que vale a pena os custos que você vê aqui.

Ainda sinto que muitos dos exemplos poderiam ser escritos de outras maneiras. Isso não adiciona qualquer expressividade irrepresentável à linguagem. Originalmente, achei que poderia ser motivado com exemplos suficientes, mas quanto mais exemplos vejo que poderiam usar esse recurso, mais me encontro concordando com @withoutboats que esse recurso simplesmente não deveria entrar na linguagem.

(Também vale a pena notar: a caixa de @nrc proc-macro-rules que usava label_break_value parece ter sido reescrita para evitá-lo.)

Uma opção é interpretar as regras atuais do rfcbot como fiz acima para significar "nenhuma caixa de seleção sinaliza sua discordância, três membros são necessários para anular a maioria", mas eu não acho que era assim que as regras eram quando foram introduzidas, então Acho que foi insincero de minha parte sugerir que você deveria seguir esse procedimento (embora eu mesmo o tenha feito em outro lugar).

Acho que esse é o procedimento correto para "se abster", mas não para fazer objeções.

IMO ele não só puxa seu peso através de uma variedade de exemplos onde é a melhor / única opção, mas a linguagem é na verdade mais simples com sua adição (já que não permitir blocos rotulados é surpreendente para mim, dado que permitimos outras construções rotuladas).

Eu normalmente acho argumentos de ortogonalidade convincentes, mas este em particular eu não acho atraente, pois pessoalmente eu teria preferido não ter rotulado quebras de loop na linguagem também.

Preocupo-me com o fato de que, com equipes cada vez maiores, perderemos a capacidade de chegar a um consenso sobre os recursos e que será difícil orientar a linguagem de maneira coerente, especialmente na equipe de design de idioma.

Eu me preocupo não apenas em dirigir, mas em parar . Há uma certa inevitabilidade que surge às vezes, onde o processo parece focado em encontrar um caminho para o "sim" e não há nenhuma borda do gráfico no fluxograma que leva ao "não", apenas "ainda não".

Muitos posts escritos em 2018 e 2019 falam sobre o crescimento de recursos na linguagem, e eu sinto que, na equipe de linguagem, precisamos levar seriamente em consideração a área de superfície total da linguagem. E eu sinto que não temos bons processos que nos incentivem a fazer isso.

@joshtriplett

Isso não adiciona qualquer expressividade irrepresentável à linguagem.

Para ser muito claro: ele faz exatamente isso. Atualmente, não há maneira de expressar este código que não interfira com outras construções de fluxo de controle, o que (como outros apontaram) é especialmente desejável em macros, onde esta seria a única construção que é inalcançável por meio de quebras não rotuladas, permitindo que os macro-autores se separem das seções sem arriscar uma sobreposição com um break fornecido pelo usuário.

ele também torna o código mais legível, já que você não precisa zig-zag (se não tivéssemos nenhum rótulo - veja o exemplo de Lua) ou fazer coisas estranhas com loops. isso também tem um pequeno benefício de desempenho, mesmo que o llvm seja capaz de otimizar o código em zigue-zague.

@joshtriplett

Ainda sinto que muitos dos exemplos _podem_ ser escritos de outras maneiras. Isso não adiciona qualquer expressividade irrepresentável à linguagem. Originalmente, achei que poderia ser motivado com exemplos suficientes, mas quanto mais exemplos vejo que _pode_ usar esse recurso, mais me encontro concordando com @withoutboats que esse recurso simplesmente não deve ser incluído na linguagem.

Isso é algo que possamos aprofundar, talvez?

Eu normalmente acho argumentos de ortogonalidade convincentes, mas este em particular eu não acho atraente, pois pessoalmente eu teria preferido não ter rotulado quebras de loop na linguagem também.

Acho essa linha de raciocínio estranha (a menos que você queira remover quebras de loop rotuladas em uma edição). Não parece apropriado basear as decisões de design com base no que você gostaria que não existisse na linguagem. Ele está lá e, portanto, devemos considerar se esse acréscimo é coerente com aquele. Caso contrário, há muitas coisas que eu poderia ter feito de forma diferente em relação ao Rust, mas não devo e não posso.

Eu me preocupo não apenas em dirigir, mas em _parar_. Há uma certa inevitabilidade que surge às vezes, onde o processo parece focado em encontrar um caminho para o "sim" e não há nenhuma borda do gráfico no fluxograma que leva ao "não", apenas "ainda não".

Ainda não é sua própria forma de "não", no sentido de que não será estabilizado sem um sim. Além disso, há um não: convença o resto de nós de que LBV é uma má ideia / não suficientemente motivada por motivos X, Y e Z. Ainda estou para ouvir muitos desses argumentos concretos. Você também demonstrou claramente aqui que não há inevitabilidade.

Muitos posts escritos em 2018 e 2019 falam sobre o crescimento de recursos na linguagem, e eu sinto que na equipe de linguagem precisamos dar _séria_ consideração à área de superfície total da linguagem. E eu sinto que não temos bons processos que nos incentivem a fazer isso.

Pessoalmente, sinto que muitas dessas mensagens são ou sobre sustentabilidade (mas não tão apropriadamente redacção @nikomatsakis fez ...) ou que muitas pessoas não entendem como a equipe linguagem funciona (ou seja, nós já fazemos dar séria consideração ao total superfície). A sintaxe da superfície total do Rust provavelmente encolheu no último ano, não aumentou. O LBV não aumenta isso notavelmente e um argumento poderia ser feito de que ele realmente reduz o número de produções na linguagem e torna a sintaxe mais uniforme.

A simples combinação de produções gramaticais não diminui a área de superfície da linguagem.

Reduzir o número de ramificações condicionais que alguém pode tomar é, sem dúvida, diminuir a área de superfície da linguagem.

Não acho que esse recurso específico funcione de qualquer maneira, como tal, provavelmente é neutro. Mas por exemplo, coisas que construções de linguagem unificar (bool permitem, alguém?) Fazer encolher área de superfície.

Pelo que vale a pena, Common Lisp, que é semelhante ao Rust no sentido de que é uma linguagem multiparadigma com macros, tem esse recurso (denominado block / return-from ). Semelhante ao que foi discutido aqui, no Common Lisp essa construção é frequentemente útil ao escrever macros e expressar o fluxo de controle irredutivelmente complexo.

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

Minha sensação é que no Common Lisp esse recurso é considerado bem-sucedido. Ele não aparece em conversas sobre recursos que tornam a linguagem difícil de aprender ou implementar.

Existe a inclusão

early return from block ⊂ exceptions ⊂ call/cc

Por exemplo, no Esquema, a seguinte emulação de return-from é viável:

(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"))

No Scheme, call/cc é visto como bem-sucedido e controverso. Acho isso particularmente interessante porque primeiro é necessário formular um objeto para poder falar sobre ele. Mesmo se você considerar call/cc como uma característica incorreta, isso tornou a linguagem mais substancial em um sentido intelectual.

A linguagem blocos nomeados .
É muito útil e conveniente.

return, break e continue são implementados como macros usando este recurso.

@jhpratt Por favor, leia a discussão nesta edição.

Repetindo alguns comentários que fiz no IRLO por sugestão de @Centril :

Ao discutir esse recurso, sugeri a seguinte inspiração (contrária à versão usual relacionada ao loop):

Os loops não são realmente minha inspiração aqui, de qualquer maneira. Quando eu olho para os blocos try, eu penso "cara, seria útil ser capaz de escrever alguns cálculos locais curtos com retornos iniciais sem ter que se dar ao trabalho de criar um encerramento". Os blocos try funcionam para o restrito "Eu quero retornar um Try impl" e não suportam nenhum tipo de rótulo.

O que está acima é meu principal caso de uso para isso: Gosto do estilo de retorno antecipado. try {} blocos try {}? para blocos aninhados) e fará você calça seu tipo em uma certa forma monádica que não é genérica (conforme evidenciado pelos casos de uso fornecidos).

Também fiz algumas observações sobre C ++, em que não interromper / continuar o rótulo é muito doloroso, especialmente na ausência de iteradores semelhantes ao Rust. Por exemplo, não há como continuar um loop externo a partir de um loop interno sem um possivelmente-UB goto ou um break mais um continue condicional fora do loop interno.

Pelo menos C ++ 's falta de um borrow-checker faz o truque em linha lambda menos doloroso, o que faz, efetivamente, o apoio LVB, desde que voltou no lambda em linha se transformou em algo como LVB por Inliner do LLVM. Algo assim é ... um pouco mais questionável em Rust.

Devo também apontar que este recurso é aproximadamente equivalente em expressividade ao goto Go, que o tempo de compilação impõe não pular declarações e outros enfeites (francamente, se Rob Pike pensasse que goto era aceitável, dado a história de tentar rebater os problemas do C ++, eu confio nele um pouco).

Além disso, se vamos entrar no estado da técnica, Kotlin também fornece esse recurso exato, na forma de return<strong i="24">@label</strong> expr; .

Em c'ish línguas, geralmente uso rótulos, mesmo sem qualquer goto de entrada, como locais para pontos de interrupção estáveis ​​em loops, porque o depurador pode reconhecer break function:label .
Mesmo sem consenso sobre o intervalo, os rótulos podem ser bons.

Edit: Um obstáculo potencial, é que normalmente os rótulos seguem a convenção de nomenclatura de símbolo, se eu entendo a RFC, esses rótulos não seguem a convenção de nomenclatura de símbolo, onde ', não é válido em um nome de símbolo.
Não tenho certeza de que, por exemplo, Dwarf ou gdb se há de fato algum problema aqui.

Edit2:
Tenho certeza de que há um pouco de fumaça nisso, se olharmos para o comportamento de citação de rótulos normais baseados em C,
no depurador, o gdb pelo menos tratará as aspas, para aspas em vez de parte do nome do símbolo. O seguinte resulta em

Ponto de interrupção 1 em 0x401114: arquivo, linha 1.
citação incomparável

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

E eu não acredito que isso possa ser afetado pelo suporte específico da linguagem rust-language no gdb, pois acredito que essa citação acontece antes da correspondência de símbolos.

Editar: O navio provavelmente navegou neste devido a rótulos de loop existentes.

Um ponto menor a favor do retorno antecipado dos blocos seria a programação de contrato do homem pobre. Basta adicionar declarações assert como pré e pós-condições. Para manter o conforto, deve ser possível substituir return por break 'ret para permitir esta construção:

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

Esta é uma solução imperfeita, entretanto, porque return deve ser proibido dentro do bloco.

Adicionando uma nota de que eu queria esse recurso, mas não o usei porque não sabia que ele existia, o que eu sinto que é um modificador negativo para "as pessoas não o querem, conforme evidenciado por poucas pessoas o usam".

Reinventei o conceito de forma independente

Eu tenho brincado com isso hoje, e é _super_ útil em blocos de async . No entanto, parece haver problemas quando combinado com o recurso 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 blocks são um erro.

... você não pode rotular o próprio ? ? (como em Err(()) 'foo?; )

Eu discordo veementemente que os blocos try são um erro, embora essa seja uma discussão separada e provavelmente não vale a pena ir e voltar aqui.

Neste exemplo específico, pode ser factível, mas é muito minimizado em comparação com o código real que tenho, onde 'foo contém um pedaço decente de código e vários ? s.

@ SoniEx2

try blocks são um erro.

Este comentário é impróprio. O comentário de @jonhoo estava relatando uma (presumivelmente) interação com bugs. Independentemente das opiniões de alguém sobre blocos de try (ou valor de quebra de rótulo), é claro que eles devem interoperar sem problemas.

eles deveriam, com a sintaxe Err(()) 'foo?; .

@jonhoo Suspeito que você esteja vendo detalhes implícitos vazando em termos de como try é desocupado - você pode arquivar isso como um problema separado e podemos mover a discussão de possíveis correções para lá?

A RFC diz que

'BLOCK_LABEL: { EXPR }

é açúcar sintático para

'BLOCK_LABEL: loop { break { EXPR } }

Tentei fazer essa substituição e o código compila, com um aviso sobre código inacessível.

#![feature(try_blocks, label_break_value)]

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

@nikomatsakis @ciphergoth arquivado como https://github.com/rust-lang/rust/issues/72483.

Acho que não tenho mais objeções a isso. Eu teria objetado mais fortemente ao conceito inicial de quebra rotulada se ele fosse considerado hoje, mas dado que esse conceito existe, não acho que faça sentido para mim continuar a contestar sua aplicação a blocos arbitrários.

(Isso se aplica ao formulário atual, usando break , e não a qualquer outra sintaxe.)

@rfcbot resolve should-close-not-merge
@rfcbot revisado

@joshtriplett Pelo que vale a pena, descobri que isso é imensamente útil em blocos de async , já que é a única maneira de fazer um "retorno antecipado". Isso significa que em vez de escrever:

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..
    }
  }
}

Eu consigo escrever:

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..
  }
}

Isso é perfeitamente análogo a como você pode retornar antecipadamente com return em funções / encerramentos e com continue/break em loops. Admito que seria bom se eu não precisasse do bloco extra ( async 'block { uma possibilidade?), Mas definitivamente supera os if-s aninhados.

Permitir que blocos assíncronos sejam anotados diretamente com rótulos parece uma extensão muito boa para esse recurso.

@rfcbot fcp cancel

Vou cancelar o FCP aqui, pois está bloqueado para sempre. Provavelmente deveríamos discutir se queremos empurrar esse futuro. Pelo menos, parece que ele deve ser atualizado para levar em conta os blocos assíncronos, e parece que isso adiciona um novo caso de uso para o recurso.

Proposta @nikomatsakis cancelada.

Observe que não há ambigüidade sobre a semântica desta proposta na presença de blocos assíncronos: a definição no RFC ainda se aplica, ou seja,

'BLOCK_LABEL: { EXPR }

é simplesmente açúcar sintático para

'BLOCK_LABEL: loop { break { EXPR } }

exceto que as interrupções ou continuações não marcadas que se vinculariam ao loop implícito são proibidas dentro do EXPR.

Observe que você pode (antecipadamente) return partir de blocos assíncronos, em vez de rotulados break , portanto, rotular blocos assíncronos não faz muito sentido:

let fut = async {
    return 42;
    0
};

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

( playground )

@WaffleLapkin Na verdade, acabei de vir aqui para observar que, recentemente, fui informado disso ! Eu acho que o recurso ainda é muito útil por ser capaz de _pular_ seções de código (não execute o resto deste bloco, mas também não retorne), mas sua aplicabilidade para async _specifically_ é menor que Eu inicialmente pensei.

Esta página foi útil?
0 / 5 - 0 avaliações