Rust: Problema de seguimiento para RFC 2046, valor de rotura de etiqueta

Creado en 27 feb. 2018  ·  135Comentarios  ·  Fuente: rust-lang/rust

Este es un problema de seguimiento para RFC 2046 (rust-lang / rfcs # 2046).

Pasos:

Preguntas sin resolver:

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

Comentario más útil

@sin barcos

Me gustaría evitar agregar opciones altamente flexibles y de aplicación más amplia a la caja de herramientas de flujo de control de Rust. Solo me interesa agregar un flujo de control que esté dirigido a casos de uso específicos e importantes y que tenga un alto impacto ergonómico.

Estoy de acuerdo con este principio, y creo que esta característica cumple con ese estándar (flujo de control familiar dirigido a casos de uso específicos e importantes). Hay varias veces en las que he revisado o escrito código como los ejemplos anteriores en los que siento que esta es, con mucho, la forma más limpia y fácil de leer de escribir este código. La reutilización de construcciones de flujo de control existentes como loop con un break incondicional al final confunde al usuario, y las funciones que se aplican inmediatamente a menudo son insuficientes debido al uso de ? , await! , u otras construcciones de flujo de control intermedias.

Usar loop + arrastrar break es confuso, y deberíamos preferir que los usuarios declaren su verdadera intención antes que exigirles que abusen de una herramienta que fue creada para un estilo diferente de flujo de control. Esto es similar al hecho de que incluso tenemos una construcción loop , mientras que otros lenguajes se contentan con while true { ... } . Hacer esto hace posible escribir código que expresa una intención más clara y, como resultado, es más legible.

Además, esta característica es algo que siempre había esperado que tuviera Rust, dado que hemos etiquetado-la mayoría-de-otras-cosas y romper-de-las-cosas-etiquetadas, el hecho de que no se puede etiquetar o romper un bloque parece confuso y mal para mí.

TL; DR: Creo que esta característica es compatible con casos de uso del mundo real que solo se pueden escribir de otra manera a través de declaraciones if fuertemente anidadas o abusando de otras construcciones como loop-break-value. Es una pequeña adición a la sintaxis de la superficie que hace que los bloques se comporten como yo esperaría que se comportaran y me permite escribir el código al que me refiero en lugar de algo mucho más complicado.

Todos 135 comentarios

El uso de "return" tendría implicaciones interesantes para las etiquetas ? (tryop questionmark operator thingy).

¿Usar retorno como palabra clave en lugar de descanso?

@ mark-im y @joshtriplett ya se han pronunciado en contra del regreso, pero me

break (y continue) solo se pueden usar con un bucle.
Si puedes romper algo, puedes continuarlo. (No creo que haya una sintaxis obvia para elegir para continuar en un bloque).

En C, C ++, Java, C #, JavaScript y probablemente en más lenguajes, normalmente está break haciendo una declaración de cambio para evitar fallos. Rust ha resuelto esto mucho mejor con | en patrones, pero las personas que provienen de esos lenguajes realmente no verán break como algo solo para bucles for. Especialmente porque Java y JavaScript también exponen la función a través de break y no return .

Sin embargo, el argumento de las "reglas para recordar" funciona muy bien en la otra dirección. Por lo que puedo decir, es una característica común de los lenguajes mencionados, así como de Rust, que el retorno solo se aplica a funciones y nada más. Entonces, si ve un retorno, sabrá que se está dejando una función.

Etiquetar un bloque no causa errores en las rupturas sin etiquetar existentes

Primero, creo que esto sucede muy raramente, ya que la función de rotura etiquetada no es algo que se use 10 veces por cada 1000 líneas. Después de todo, solo se aplicará a las rupturas sin etiquetar que cruzarían el límite del bloque, no a las rupturas sin etiquetar dentro del bloque. En segundo lugar, los usuarios de Rust están acostumbrados a las quejas / mensajes de error del compilador, ¡con gusto los solucionarán! En tercer lugar (creo que este es el punto más fuerte), si en lugar de etiquetar un bloque lo envuelve en un bucle, ya debe tener cuidado con las rupturas no etiquetadas y no hay un mensaje de error que enumere convenientemente las declaraciones de ruptura, tiene buscarlos usted mismo :).

Especialmente porque Java y JavaScript también exponen la función a través de interrupciones y no regresan.

Este para mí es el punto decisivo. romper con los bloques es una cosa en muchos idiomas. Regreso de cuadras ... no tanto.

Personalmente, comparto la opinión de @joshtriplett sobre el uso de break lugar de return , pero me pareció que la discusión no se había resuelto en el RFC ... Si cree en la pregunta se resuelve en el equipo de lang, marque la casilla con una nota =)

Solo digo que estoy trabajando en esto. No necesito instrucciones de un mentor. Solo para no duplicar esfuerzos. Espere un PR pronto.

Todavía estoy a favor de return sobre break , pero puedo estar de acuerdo en no estar de acuerdo aquí. Pregunta resuelta.

Actualmente (con rustc 1.28.0-nightly (a1d4a9503 2018-05-20) ) rustc no permite unsafe en un bloque etiquetado. ¿Es esto esperado?

@topecongiro Sí, creo que es intencional que esto esté permitido actualmente solo en bloques simples. Podría cambiar en el futuro, pero dado que se trata de una característica inusual y de bajo nivel, me inclino a que sea una característica en lugar de una restricción. (En el extremo, ciertamente no quiero else 'a: { .)

Definitivamente de acuerdo. El flujo de control inseguro + inusual suena como algo para desanimar.

Sin embargo, en caso de apuro, podrías usar:

'a: {
unsafe {...}
}

¿Correcto?

En realidad, aunque más crea un nuevo ámbito léxico, no es un bloque. Todo el if-else es un bloque (un poco). Entonces no, no obtendrías else 'a: { , obtendrías 'a: if ... else { .

else contiene un bloque (expresión). no hay un "nuevo ámbito léxico" sin bloques.
Una posición de sintaxis superficial incluso peor que else sería 'a: while foo 'b: {...} .
(curiosamente, continue 'a es break 'b , es posible que queramos confiar en eso al menos internamente)

(curiosamente, continue 'a es break 'b , es posible que queramos confiar en eso al menos internamente)

¡Qué gran observación!

Creo que las etiquetas deberían ser parte de expresiones que contengan bloques, no bloques en sí mismos. Ya tenemos un precedente para esto con loop . (Da la casualidad de que un bloque simple en sí mismo también es una expresión que contiene bloques. Pero cosas como if y loop son expresiones que contienen bloques sin ser bloques, supongo).

(Cosas como while o for no deberían admitir label-break-value, porque podrían o no devolver un valor en función de si se completan normalmente o con un break .)

@eddyb

(curiosamente, continúe con 'a is break' b, es posible que queramos confiar en eso al menos internamente)

Solo si break 'b vuelve a verificar la condición del bucle ...

@ mark-im Es equivalente a 'a: while foo {'b: {...}} , el break no verificaría la condición del bucle, el bucle sí lo haría, porque la condición del bucle se verifica antes de cada iteración del bloque del cuerpo.

Woah, lo encuentro _muy_ poco intuitivo. Espero que break 'b sea ​​básicamente goto 'b , lo que significa que nunca salimos del cuerpo del bucle y la condición no se vuelve a verificar ...

Oh: man_facepalming: Ya veo ...

Es por eso que no me gusta la etiqueta romper / continuar: /

Bueno, específicamente no tenemos la capacidad de etiquetar estos extraños bloques internos, así que no veo el problema. break siempre significa "dejar este bloque" y, dada la restricción anterior, no hay forma de que eso signifique otra cosa que "ir al lugar después de la llave de cierre asociada".

Mi confusión no fue específica de bloques internos extraños, pero realmente no quiero reabrir la discusión. Eso ya pasó y la comunidad decidió agregarlo.

De acuerdo, entiendo que la accesibilidad es un gran problema con los lenguajes de programación ... sin embargo, la rotura etiquetada es extremadamente útil si escribes código como yo.

Entonces, ¿cómo podemos hacer que las roturas etiquetadas sean más accesibles?

Entonces, ¿cómo podemos hacer que las roturas etiquetadas sean más accesibles?

Esa es una gran pregunta. Algunas ideas que tuve:

  • Deberíamos recopilar algunas muestras de cómo la gente usa esto en la naturaleza. Podemos buscar patrones indeseables o hábitos perezosos.
  • Deberíamos tener un estilo obstinado cuando se considera una mala práctica usar la etiqueta romper / continuar.
  • Es posible que podamos agregar lints para algunos patrones que podrían convertirse mecánicamente en combinadores de bucles / iteradores (aunque no puedo pensar en ninguno de esos patrones en la parte superior de mi cabeza).

Como primera muestra (ciertamente sesgada), mi último (y primer) encuentro con la rotura etiquetada en el código real no fue estelar: https://github.com/rust-lang/rust/pull/48456/files#diff -3ac60df36be32d72842bf5351fc2bb1dL51. Respetuosamente sugiero que si el autor original hubiera puesto un poco más de esfuerzo, podría haber evitado por completo el uso de rotura etiquetada en ese caso ... Este es un ejemplo del tipo de práctica que me gustaría desalentar si fuera posible.

¿Eso ... no está etiquetado como rotura?

Con respecto a cómo break y continue mezclan en esto, eso también fue mencionado en la discusión de RFC original por el autor de RFC; ver https://github.com/rust-lang/rfcs/pull/2046#issuecomment -312680877

Supongo que el nombre break es subóptimo, pero está bien establecido para los bucles. Un enfoque más "basado en principios" sería usar la sintaxis return 'a value , que inmediatamente continúa la ejecución "después" del bloque 'a , usando el valor value para ese bloque.

@ mark-im "no está usando una función porque no es accesible" no está "haciendo que dicha función sea accesible".

¿Cómo podemos modificar la rotura etiquetada para que obtenga todo el poder expresivo del lenguaje y, al mismo tiempo, deje de quejarse de que su cerebro no puede procesar las cosas de control de flujo tan bien como el compilador?

(Además, el código que vinculó no parece estar relacionado con RFC 2046 / label-break-value ... ¿quizás vinculó el código incorrecto?)

¿Eso ... no está etiquetado como rotura?

(Además, el código que vinculó no parece estar relacionado con RFC 2046 / label-break-value ... ¿quizás vinculó el código incorrecto?)

Eso es cierto, era una continuación etiquetada normal antes de que la cambiara, pero creo que existen los mismos problemas (y posiblemente son peores ya que el flujo de control del resto de una rutina puede verse afectado por el valor que devuelva).

@ mark-im "no está usando una función porque no es accesible" no está "haciendo que dicha función sea accesible".

¿Cómo podemos modificar la rotura etiquetada para que obtenga todo el poder expresivo del lenguaje y, al mismo tiempo, deje de quejarse de que su cerebro no puede procesar las cosas de control de flujo tan bien como el compilador?

Lo siento, no me refiero a quejarme. Si soy la única persona que se siente así, no me importa hacerme a un lado.

En mi humilde opinión, este es un problema fundamental con la etiqueta romper / continuar: es _demasiado_ expresivo, y la única forma que conozco de mitigarlo es recomendar el uso como "buen estilo" (lo que sea que eso signifique). Por ejemplo, "solo use una ruptura etiquetada con un valor desde el principio o el final del cuerpo de un bucle (no en el medio)". Esto significaría que las posibles formas de salir de un bucle con un valor son fáciles de detectar y razonar.

Así es como evito ir a / rotura etiquetada en otros idiomas: https://gist.github.com/SoniEx2/fc5d3614614e4e3fe131/#file -special-lua-L4-L72

¿Es más legible?

Si es así, quizás podamos encontrar algún tipo de sistema condicional basado en bloques etiquetados. Similar a esto , tal vez.

El punto de ruptura y continuación sin etiquetar es proporcionar los casos en los que no se puede poner fácilmente la condición / valor en el encabezado del bucle. Algunos bucles son simplemente mucho más sencillos, legibles, rápidos, etc. con la ruptura en el medio.

El punto de rotura etiquetada y continuar en bucles es similar; a veces, la única alternativa es introducir una nueva variable, una nueva función (abusando así de return ) o alguna otra contorsión que solo hace que las cosas sean más difíciles de seguir, sin embargo desafortunadamente, una rotura etiquetada puede ser complicada.

Pero esas dos características no son de lo que trata este hilo. Son bastante universales, precisamente por las mejoras que ofrecen para expresar un flujo de control intrínsecamente complejo. En cambio, este hilo trata de salir de un bloque que no es de bucle. Esto es ciertamente más novedoso, y es posible que la gente no sepa buscar un break fuera de un bucle, aunque requerir una etiqueta significa que la señal todavía está allí una vez que sepa lo que significa.

Esto es lo que quise decir con su ejemplo, @ mark-im: era un uso bastante estándar de un bucle etiquetado, en lugar de un bloque etiquetado.

Un enfoque más "basado en principios" sería usar la sintaxis return 'un valor, que inmediatamente continúa la ejecución "después" del bloque' a, usando el valor de valor para ese bloque.

Nota al margen sobre break vs return : Prefiero break porque es un flujo de control estático. return es dinámico, en el sentido de que vuelve a la persona que llama, que puede estar en cualquier lugar. Significa "He cumplido con mi responsabilidad, no hay ningún otro lugar donde mirar para ver lo que hago". break siempre va a algún lugar que tenga un alcance léxico, al igual que con los bucles.

Creo que return 'label expr lee muy bien desde una perspectiva de "haz lo que digo" en el sentido de que se puede pensar como "volver expr a la 'etiqueta de ubicación" . No creo que break 'label expr lea igualmente bien en esta perspectiva ...

Por lo tanto, al aislarme de otros lenguajes de programación, podría haber abogado por return 'label expr . Sin embargo, dado el flujo de control en otros idiomas, reutilizar return convierte de repente en una opción mucho menos viable y esto me inclina a favor de break 'label expr .

Soy firmemente de la opinión de que debería ser break 'label expr y no return 'label expr . Hacer lo contrario sería totalmente inconsistente con nuestro uso actual de break 'label en bucles.

@ SoniEx2 Creo que prefiero el fragmento que publicaste, en gran parte porque las variables son una buena forma de documentar los invariantes de bucle. OTOH, podría ser posible hacer lo mismo con los nombres de las etiquetas (es decir, cada vez que se ingresa este bloque etiquetado, la P invariante se mantiene). Supongo que este es un lugar donde sería bueno tener algunas muestras de código más de la naturaleza ...

Propuesta de estabilización

La función se implementó en https://github.com/rust-lang/rust/pull/50045 por @ est31 y ha estado disponible todas las noches desde 2018-05-16 (+17 semanas), por lo que se ha horneado lo suficiente para la estabilización. Además, no se han reportado problemas desde que se fusionó el RP que implementó esto. Todas las preguntas sin resolver también se han resuelto desde entonces, y existe un fuerte consenso de que break debería ser la sintaxis superficial en lugar de return .

Por lo tanto, me muevo para estabilizar el valor de rotura de etiqueta (RFC 2046).

@rfcbot fusionar

Reporte

  • Puerta de función:

    • 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
  • Pruebas:

    • 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_unlaoted_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 preocupa FIXME-in-tests

El último archivo de prueba tiene actualmente un FIXME:

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

Esto debe resolverse antes de estabilizarse.
Escribí algunas pruebas para comprobar que el comportamiento esperado era correcto. el FIXME está 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);
}

Se deben agregar pruebas similares a estas antes de pasar del FCP propuesto al FCP.

El miembro del equipo @Centril ha propuesto fusionar esto. El siguiente paso es la revisión por parte del resto de equipos etiquetados:

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

Preocupaciones:

  • costo-beneficio (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)

Una vez que la mayoría de los revisores aprueben (y ninguno objete), entrará en su período de comentarios final. Si detecta un problema importante que no se ha planteado en ningún momento de este proceso, ¡hable!

Consulte este documento para obtener información sobre los comandos que pueden darme los miembros del equipo etiquetados.

@rfcbot se preocupa por el costo-beneficio

De la propuesta RFC FCP :

Otro grupo consideró que el "costo-beneficio" de esta característica para el idioma no fue suficiente. En otras palabras, la mayor complejidad que traería la función, dicho de otra manera, la posibilidad de que la gente realmente la use y tuvieras que leer su código, supongo, así como el tamaño general del lenguaje. no estaba a la altura de su utilidad.

Todavía no creo que debamos tener esta función en absoluto. ¿Desde que se implementó se ha utilizado mucho? En los casos en que se ha utilizado, ¿es significativamente peor utilizar funciones? Si hay un beneficio aquí, ¿supera el costo de hacer que el lenguaje sea más grande y más complejo?

@nrc Comparto la misma preocupación. Entiendo el argumento para tenerlo disponible para que las macros puedan usarlo, pero al mismo tiempo, preferiría no tener esto en absoluto.

No quiero a los argumentos de recauchutado de la rosca original RFC, pero yo creo que esto es un punto razonable para preguntar acerca de experiencias con esta característica. ¿Qué usos ha visto?

analizadores sintácticos escritos a mano, en su mayoría ... (me gustan mis analizadores sintácticos escritos a mano>. <)

sería más útil / más fácil de usar con label-propagate ( try_foo() 'bar? ).

@rfcbot se refiere a casos de uso

Resumiendo un poco la discusión sobre Discord: nos gustaría ver casos de uso concretos para esto, a partir del código real, que no se ven más claramente cuando se reescriben para no usar esta función.

~ FWIW, mi implementación de EXPR is PAT basa en que break 'label value esté disponible al menos en AST, no sé cómo podría funcionar el desugaring sin él.
La implementación de EXPR? en el compilador también se basa en break 'label value . ~

~ Es una especie de bloque de construcción básico para funciones de flujo de control más grandes, por lo que puede ser necesario implementarlo en el compilador de todos modos.
Entonces, el "costo" aquí probablemente sea simplemente hacer que esté disponible en la sintaxis superficial. ~

EDITAR: He malinterpretado totalmente el problema, pensé que se trata de loop { ... break 'label value ... } , no de bloquear { ... break 'label value ... } .
Nunca tuve la oportunidad de probar este porque siempre olvido que ya está implementado.

@petrochenkov Hablando con @joshtriplett en Discord, señalaron que estaban preocupados por la complejidad de cara al usuario, no por la implementación del lenguaje.

Creo que el aumento de complejidad es mínimo: para los lectores, debería ser obvio lo que esto significa porque el concepto ya existe en bucles, etc.
De lo contrario, tendría que usar un bucle con una declaración de interrupción incondicional, que es menos clara y, de hecho, hay incluso una pelusa cortante (never_loop) sobre esto. Entonces creo que hay un beneficio.

En cuanto a los casos de uso, ese tema ya ha surgido en el RFC. Señalé mi caso de uso aquí . También vea el caso de uso enumerado por @scottmcm directamente a continuación. Quizás haya más en el hilo, idk. @joshtriplett, ¿eso resuelve la pregunta del caso de uso?

Estoy de acuerdo con @nrc y @joshtriplett y también quiero plantear una inquietud sobre el proceso aquí: aceptamos tentativamente este RFC con una advertencia explícita de que la estabilización se bloqueó al revisar las preguntas que @nrc y @joshtriplett han planteado, pero @Centril 's La propuesta de fusión no menciona en absoluto este problema de bloqueo y la trata como una fusión muy estándar de "función horneada". No culpo a

Fue preocupante para mí en términos de todos nuestros procesos ver que esto pasó 2 días y medio sin que se mencionara la preocupación por el bloqueo, y que la mayoría de los miembros del equipo ya habían marcado su casilla. Es concebible, dado que ya no requerimos el consenso activo de todos los miembros, que esto podría haber ingresado al FCP sin que el bloqueador ni siquiera fuera levantado. Esto se siente como una subversión del acuerdo anterior que me llevó a consentir en fusionar el RFC, y creo que es completamente causado por un seguimiento insuficiente de la información.

@withoutboats Sí, exactamente. Esto me hace un poco menos inclinado, en el futuro, a aceptar las cosas sobre una base de "vamos a XYZ durante el proceso de estabilización", hasta que tengamos algún proceso en el lugar que hace que sea extremadamente improbable que se pase por alto.

@sin barcos

No culpo a

No obstante, disculpas; No estaba al tanto de la advertencia, pero debería haber comprobado tales cosas de todos modos cuando creé el problema de seguimiento.

Esto se siente como una subversión del acuerdo anterior que me llevó a consentir en fusionar el RFC, y creo que es completamente causado por un seguimiento insuficiente de la información.

Debería haberle hecho una pregunta sin resolver; Creo que el error del proceso ocurrió en ese momento.
En cuanto a cómo mejorar el proceso; Creo que es importante que las preguntas sin resolver lleguen al


En cuanto a la propuesta fcp-merge; Personalmente, creo que sería útil por razones de uniformidad y para su uso por macros. Sin embargo, si cree que es demasiado pronto para proponer una estabilización; no dude en cancelar la propuesta :)

@ est31

De lo contrario, tendría que usar un bucle con una declaración de interrupción incondicional,

O reestructurar el código para evitarlos.

Señalé mi caso de uso aquí .

¿Por qué no reemplazar break 'pseudo_return por return Ok(vectors); ?

como he mencionado aquí , esto es útil para analizadores escritos a mano (incluso sin etiquetado-propagar ( try_foo() 'bar? )).

label-break-value permite la fácil imperativaización de código que de otro modo sería funcional. en general, el código imperativo tiende a ser más legible que el código funcional.

O reestructurar el código para evitarlos.

Por supuesto, Rust se está completando. Pero la reestructuración puede resultar un poco difícil. En general, puede rechazar casi todas las características del azúcar (y esta es la característica del azúcar) sobre la base de que "puede usar las formas existentes".

¿Por qué no reemplazar el break 'pseudo_return con return Ok (vectores) ;?

En realidad, en este caso tiene razón, se puede reemplazar la ruptura con un retorno Ok. Pero en otros casos, es posible que desee realizar el procesamiento posteriormente, etc. El comprobador de préstamos funciona mal a través de los límites de las funciones, no puede factorizar todos y cada uno de estos bloques en una función.

De todos modos, he roto mi silencio sobre comentar las características del lenguaje a través de medios oficiales, y lo lamento. Todos los mismos puntos, repetidos una y otra vez. Esta mierda es una pérdida de tiempo, lo siento. Así que no esperes más comentarios de mi parte.

@ est31 Realmente aprecio que proporciones detalles; gracias.

Existe un problema de accesibilidad para probar y usar estas cosas, debido al requisito de Rust nocturno.

Apunto estable. Espero que esto se pueda solucionar algún día.

Si queremos casos de uso, aquí hay uno que mencioné hace un tiempo; básicamente una elaboración del "procesamiento posterior" de @ est31 : escribí un lexer que maneja prefijos literales de cadenas C ++ (en el caso real de C ++, una explosión combinatoria de { L , u8 , u , U ,} { R ,}) y tokens de varios bytes que tienen "espacios" en la cantidad de bytes utilizados (no estoy seguro de que uno tenga sentido sin el ejemplo). La función get_token actualmente se ve así:

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 las pirámides de _ => self.identifier(rest) s (repetidas cuatro veces para u , U , R y L ) y _ => self.make_token(Dot, rest) s, formando una especie de estilo de paso de continuación donde identifier , string_literal , etc. todos deben llamar también make_token .

Me hubiera gustado consolidar las cosas a un estilo menos de continuación usando break -from-block, y casi lo hice a través de la etiqueta loop s, pero consideré que esa versión era demasiado extraña para leerla. Para ser más especifico:

  • Habría movido todas las llamadas make_token a una sola ubicación después de la principal match decode_byte(self.source) , y la hubiera alineado, es pequeña y contiene unsafe con sus invariantes confirmados por get_token .
  • Habría usado break 'label self.string_literal(..) para cortocircuitar una vez encontrando un " o ' , y luego combinado todas las llamadas self.identifier(..) hasta el final de ese brazo de coincidencia .

    • Es posible que también haya podido linealizar la explosión combinatoria de prefijos: verifique u / u8 / U / L , luego verifique R . Esto usa menos break 'label s, pero sigue siendo un puñado.

  • Hubiera usado break 'label (Ellipsis, rest) para hacer un cortocircuito una vez encontrando un ... , y luego combinado ambos (Dot, rest) s hasta el final de ese brazo de coincidencia.

En general, esto es básicamente el "flujo de control plano con if + retorno temprano", sin el requisito de extraer cosas en una función separada. Eso es extremadamente valioso en este caso por algunas razones:

  • La mayoría de estas funciones serían funciones diminutas de un solo uso sin buen nombre, que solo servirían para hacer las cosas aún menos legibles que este estilo de continuación.
  • Algunas de estas funciones requerirían un montón de parámetros adicionales que, de lo contrario, serían simples locales con tipos inferidos.

    • O alternativamente, cierres, que en algunos de estos casos causarían problemas de verificación de préstamos.

  • Como mencioné anteriormente, aquí se mantienen invariantes de seguridad en todas las funciones. Cuanto más código de estado-máquina de línea recta sea aquí, mejor, especialmente porque la gente regresa más tarde para realizar pequeños cambios en los nuevos tipos de tokens.

Escribiría todo, pero supongo que nunca cometí ese intento (probablemente porque me encontré con todos los problemas que enumeré anteriormente) y ya he gastado suficientes palabras aquí. :)

@SergioBenitez, ¿ podría explicarnos el uso que hace http://rocket.rs/ de label_break_value y sus puntos de vista sobre la estabilización?

@Centril ¡Claro! Aquí está la esencia:

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 esta función, evito:

  • Dividir el bloque en una función diferente para "retornos" tempranos.
  • Agregar Transform::Borrowed a cada valor de "retorno" en el bloque.
  • Posible incorrección si se devuelve un Transform diferente en diferentes casos.

    • Nota: Este es un invariante particular de Rocket y este fragmento de código.

Estaba muy feliz de ver que esto existía. Esto es exactamente lo que quiero escribir. Dicho esto, claramente puedo escribir esto de manera diferente para no depender de esta característica.

@SergioBenitez ¡Gracias! Me pregunto si podrías (eventualmente) reescribir esto con try { .. } . Al igual que:

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 Sí, eso funcionaría siempre que solo haya una ruta de éxito, que es el caso aquí.

O reestructurar el código para evitarlos.

Al hacer esto, corre el riesgo de agregar ramas adicionales o llamadas a subrutinas que el optimizador no puede resolver. Para mí, tal transformación de flujo de control parece ser una tarea bastante complicada.

En los casos en que se ha utilizado, ¿es significativamente peor utilizar funciones? Si hay un beneficio aquí, ¿supera el costo de hacer que el lenguaje sea más grande y más complejo?

Es posible que una pequeña función esté insertada, pero, sin embargo, obtendría un exceso de código innecesario o, digamos, duplicaciones de código tontas.

Preferiría el código binario más elegante posible. Puede hacerlo mediante un flujo de control un poco más avanzado, casi lo mismo que un retorno anticipado de una función.

Me pregunto si podrías (eventualmente) reescribir esto con try {..}. Al igual que:
[...]

Eso parece un poco confuso, ya que se introducen ramas, solo para optimizarlas. Pero en algunas situaciones, es posible que se necesiten ambos. Por lo tanto, teniendo

'a: {try{...}}

o

'a: try {...}

sería bueno.

Nos gustaría ver casos de uso concretos para esto, a partir del código real, que no se ven más claramente cuando se reescriben para no usar esta función.

Por favor, siéntete libre de deshonrarme:

Puede que no sea el mejor código, pero quería que todo funcionara.

Me gustaría reafirmar experimentalmente el bucle principal como una máquina de estado en términos de estilo de paso de continuación. Pero las continuaciones y las llamadas de cola forzadas son otro tema.

Sería muy bueno ver esta función estabilizada. Tengo numerosos casos de uso en los que esta sintaxis simplifica y aclara la intención de manera significativa. Para secciones largas que implican realizar operaciones falibles y desenvolver sus resultados, esta sintaxis es excelente.

Estoy de acuerdo con @zesterer en que la estabilización de esta función sería un beneficio para el ecosistema y haría que ciertos patrones fueran menos molestos de escribir: +1:

FWIW, lo usé recientemente en el compilador por primera vez.
El patrón es el mismo que en los ejemplos anteriores: estamos verificando múltiples condiciones y dejamos de hacer lo que estamos haciendo si alguna de ellas es verdadera.
https://github.com/rust-lang/rust/blob/21f26849506c141a6760532ca5bdfd8345247fdb/src/librustc_resolve/macros.rs#L955 -L987

@erickt también escribió un código que quería usar esto por la misma razón: verificar múltiples condiciones, romper una vez que una de ellas se vuelva falsa. Puede fingir esto con cierres de llamada inmediata o let _: Option<()> = try { ... None?; .. }; pero ambos son bastante hacky.

@withoutboats @nrc @joshtriplett ¿Tiene alguna idea sobre los casos de uso presentados hasta ahora por varias personas?

Haga ping a @nrc @joshtriplett :) - en la última reunión de lang discutimos la posibilidad de que todos intenten reescribir algunos de los ejemplos dados anteriormente y mostrarnos cómo los estructuraría.

Me sentí estimulado por la publicación del blog de greydon para escribir un comentario aquí sobre por qué no quiero estabilizar esta función. Siento que ha sido un poco injusto por mi parte haber aceptado este experimento; Es difícil demostrar que es negativo, pero no tengo idea de qué tipo de código de muestra podría superar mi oposición a agregar esta forma de flujo de control al lenguaje.

Mi objeción general es que simplemente creo que Rust no necesita más construcciones de flujo de control de ramificación abiertas. Tenemos coincidencia y bucle, y luego encima de esos tipos de datos algebraicos, azúcar de sintaxis, abstracción de funciones y macros para crear una gran variedad de patrones de flujo de control para que los maneje cualquier usuario común. Esto ya se siente como si se volviera abrumador, y personalmente he tenido que adoptar algunas reglas para manejar la complejidad del árbol de decisiones (por ejemplo, tengo una regla para nunca alcanzar los combinadores como primer paso: solo si es obvio después de la de hecho, sería más agradable como combinadores).

Me gustaría evitar agregar opciones altamente flexibles y de aplicación más amplia a la caja de herramientas de flujo de control de Rust. Solo me interesa agregar un flujo de control que esté dirigido a casos de uso específicos e importantes y que tenga un alto impacto ergonómico. En mi opinión, esta construcción tiene los atributos exactamente invertidos: es ampliamente aplicable, extremadamente maleable y solo ligeramente más conveniente que las alternativas.

Además, creo que esta característica tiene otra cualidad realmente negativa: la irregularidad debido a la compatibilidad con versiones anteriores. Es muy irregular que break salga de los bloques solo si están etiquetados, cuando salga de los bucles no etiquetados. Esto hace que la característica sea más difícil de entender de lo que sería si fuera regular, exacerbando los atributos negativos exactamente donde creo que son los peores: la comprensibilidad.

También creo que hay muchas características mucho más importantes en las que centrarnos, y dado que tenemos una clara división al respecto, preferiría posponer cualquier examen en lugar de intentar pasar por un largo proceso de consenso sobre esta propuesta.

Creo que lo correcto es encontrar cada caja que use esta función y reescribir el código para no usar esta función, entonces.

@withoutboats Gracias por articular de manera muy eficaz muchas de las mismas objeciones que yo también sostengo.

Voy a seguir adelante y ser audaz aquí:

@rfcbot cancelar
@rfcbot posponer

Propuesta de @joshtriplett cancelada.

El miembro del equipo @joshtriplett ha propuesto posponer esto. El siguiente paso es la revisión por parte del resto de equipos etiquetados:

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

No hay preocupaciones actualmente enumeradas.

Una vez que la mayoría de los revisores aprueben (y ninguno objete), entrará en su período de comentarios final. Si detecta un problema importante que no se ha planteado en ningún momento de este proceso, ¡hable!

Consulte este documento para obtener información sobre los comandos que pueden darme los miembros del equipo etiquetados.

@sin barcos

Me gustaría evitar agregar opciones altamente flexibles y de aplicación más amplia a la caja de herramientas de flujo de control de Rust. Solo me interesa agregar un flujo de control que esté dirigido a casos de uso específicos e importantes y que tenga un alto impacto ergonómico.

Estoy de acuerdo con este principio, y creo que esta característica cumple con ese estándar (flujo de control familiar dirigido a casos de uso específicos e importantes). Hay varias veces en las que he revisado o escrito código como los ejemplos anteriores en los que siento que esta es, con mucho, la forma más limpia y fácil de leer de escribir este código. La reutilización de construcciones de flujo de control existentes como loop con un break incondicional al final confunde al usuario, y las funciones que se aplican inmediatamente a menudo son insuficientes debido al uso de ? , await! , u otras construcciones de flujo de control intermedias.

Usar loop + arrastrar break es confuso, y deberíamos preferir que los usuarios declaren su verdadera intención antes que exigirles que abusen de una herramienta que fue creada para un estilo diferente de flujo de control. Esto es similar al hecho de que incluso tenemos una construcción loop , mientras que otros lenguajes se contentan con while true { ... } . Hacer esto hace posible escribir código que expresa una intención más clara y, como resultado, es más legible.

Además, esta característica es algo que siempre había esperado que tuviera Rust, dado que hemos etiquetado-la mayoría-de-otras-cosas y romper-de-las-cosas-etiquetadas, el hecho de que no se puede etiquetar o romper un bloque parece confuso y mal para mí.

TL; DR: Creo que esta característica es compatible con casos de uso del mundo real que solo se pueden escribir de otra manera a través de declaraciones if fuertemente anidadas o abusando de otras construcciones como loop-break-value. Es una pequeña adición a la sintaxis de la superficie que hace que los bloques se comporten como yo esperaría que se comportaran y me permite escribir el código al que me refiero en lugar de algo mucho más complicado.

@cramertj Lo siento, estoy un poco confundido. ¿Parece que tú y @withoutboats / @joshtriplett están diciendo exactamente lo contrario?

(fwiw, estoy de acuerdo con @withoutboats / @ joshtriplett)

@ mark-im No estoy de acuerdo con ellos en que esta función debería cerrarse. Creo que debería fusionarse (como lo indican mis comentarios y la casilla de verificación marcada arriba).

Estoy de acuerdo con @withoutboats en que no deberíamos agregar nuevas herramientas desconocidas que no estén motivadas por casos de uso específicos e importantes. Creo que esta función le resultará familiar y está motivada por casos de uso específicos e importantes.

@sin barcos

Siento que ha sido un poco injusto por mi parte haber aceptado este experimento; Es difícil demostrar que es negativo, pero no tengo idea de qué tipo de código de muestra podría superar mi oposición a agregar esta forma de flujo de control al lenguaje.

No creo que la barra que establecimos fue "muéstranos que no podría existir ningún código que pueda escribirse mejor con label-break-value"; es "muéstranos que el código específico para el que queremos label-break-value podría ser escrito más claramente de otra manera ". Esta conversación comenzó cuando usted y otros pidieron ejemplos motivadores de dónde es útil esta función, y muchas personas en este hilo (incluyéndome a mí) han proporcionado ejemplos. No fueron convincentes para usted ni para @joshtriplett , por lo que ahora les pregunto cómo escribirían ambos estos ejemplos sin label-break-value. ¿Está de acuerdo en que los ejemplos se escriben mejor utilizando label-break-value? Si es así, ¿opina usted que no son lo suficientemente comunes como para compensar el costo de permitir a los usuarios escribir código de ruptura a bloque potencialmente complicado?

Voy a seguir adelante y ser audaz aquí:

@rfcbot cancelar

Propuesta de @scottmcm cancelada.

Es muy irregular que la ruptura se salga de los bloques solo si están etiquetados, cuando se rompen los bucles sin etiqueta.

No estoy seguro de si se mencionó en el hilo o no, pero los bloques que no pueden ser dirigidos por break; hacen que los bloques etiquetados sean cualitativamente más poderosos que los etiquetados loop s en cierto sentido.

Con bloques etiquetados puede crear una macro de flujo de control que admita break; s dentro de ella, con la macro infra completamente invisible para el usuario.
Con bucles etiquetados, siempre existe el riesgo de que se use break; sin etiqueta y sea capturado por la macro infra en lugar del objetivo previsto.

En mi prototipo de EXPR is PAT usé loop s habituales para desugaring inicialmente, pero las cosas se rompieron debido al problema mencionado anteriormente, no pude arrancar el compilador en particular.
Los bloques etiquetados no se implementaron en ese entonces, así que tuve que introducir un " loop " especial en AST y usarlo en el desugaring.

Como lo indica mi casilla de verificación, estoy a favor de estabilizar esto ahora mismo. Creo que esta es una extensión natural del lenguaje que facilita las macros y controla el flujo que no se ajusta tan fácilmente a los patrones funcionales. Incluso aunque NLL hace que las vidas no sean léxicas, creo que poder anotar bloques con vidas también me parece pedagógicamente útil, al igual que la adscripción de tipos.

Sin embargo, dado que ha sido difícil lograr el consenso, en aras de encontrarlo, sugeriría que intentemos acelerar el trabajo en try { ... } , así como acelerar el trabajo para experimentar con https://github.com / rust-lang / rust / issues / 53667 (o simultáneamente con la sintaxis EXPR is PAT @petrochenkov).

Lo siento, ¿cuál es el estado de esto ahora? ¿Estamos en FCP o no?

@ mark-im Dado que ninguna de las etiquetas proposed-final-comment-period o final-comment-period se refieren al tema, no estamos en FCP ni en una propuesta para una.

(y nunca hemos estado en FCP, aunque hubo una propuesta inicial para ingresar a FCP con todas las casillas menos tres marcadas)

@scottmcm

Voy a seguir adelante y ser audaz aquí:

Esta repetición de la redacción de

Estoy convencido de que, de hecho, es una buena idea. He estado muy en contra de esta característica anteriormente, y todavía no creo que sea una característica que los programadores deberían usar. Los casos de uso anteriores son algo persuasivos, pero creo que aún podrían tenerse mejor en cuenta en funciones más pequeñas. El caso que encontré realmente persuasivo es el resultado de las macros. Si el usuario puede insertar return alguna manera, esto excluye la traducción a funciones. Sin embargo, lo que realmente queremos es simplemente goto . Me pregunto si la solución 'grande' son las macros que pueden generar HIR, en lugar de tokens, pero eso está un poco por ahí. Mientras tanto, sería bueno tener esta función para los autores de macros, así que, en general, creo que deberíamos estabilizarnos.

¿Alguien ha intentado reescribir las cosas de una manera más lenta, menos intuitiva y menos ergonómica?

Romper bloques es como un goto ergonómico. No tiene casi ninguno de los problemas de goto, pero es igual de poderoso.

Basado en el cambio de opinión de @nrc , me muevo, una vez más, para estabilizar label_break_value .
Mi informe se puede encontrar en https://github.com/rust-lang/rust/issues/48594#issuecomment -421625182.

@rfcbot fusionar
@rfcbot preocupa FIXME-in-tests

Para asegurarme de que @joshtriplett y @withoutboats tengan tiempo para plantear cualquier inquietud que aún puedan tener, registraré dicha inquietud. Lo levantaré una vez que me digan que no tienen tales preocupaciones o cuando uno de ellos ha planteado su propia preocupación.

@rfcbot se preocupa por dar-a-los-barcos-y-josh-tiempo-para-plantear-cualquier-preocupación-que-puedan-tener-todavía

Como nota de proceso para este tema y para todos los demás, evite cancelar propuestas de un lado a otro ... si cree que algo no debería avanzar, solo use las preocupaciones al respecto.

El miembro del equipo @Centril ha propuesto fusionar esto. El siguiente paso lo revisan el resto de los miembros del equipo etiquetados:

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

Preocupaciones:

Una vez que la mayoría de los revisores aprueben (y como máximo 2 aprobaciones estén pendientes), entrará en su período de comentarios final. Si detecta un problema importante que no se ha planteado en ningún momento de este proceso, ¡hable!

Consulte este documento para obtener información sobre los comandos que pueden darme los miembros del equipo etiquetados.

Bloqueo de preocupación de

Ya he expresado mi posición, que es que Rust no debería tener esta función. Dudo que esta sea una prioridad lo suficientemente alta para el proyecto como para poder dedicar el tiempo a participar en un proceso de consenso sobre esta propuesta en cualquier momento en el futuro cercano.

@rfcbot resuelve dar-botes-y-josh-tiempo-para-plantear-cualquier-inquietud-que-puedan-tener-todavía

Si es que importa: Zig también tiene esta función .

El 5 de enero de 2019 a las 9:18:29 a.m.PST, Mazdak Farrokhzad [email protected] escribió:

@rfcbot resolver
dar-botes-y-josh-tiempo-para-plantear-cualquier-inquietud-que-puedan-tener-todavia

Ya hemos planteado preocupaciones y equivalen a "esto no debería estar en el idioma". Esas preocupaciones no van a desaparecer.

Prefiero la noción de nrc de que las macros deberían poder generar IR. Eso parece una solución completamente razonable para esto y muchas posibilidades futuras, sin necesariamente agregar a la sintaxis superficial del lenguaje.

@joshtriplett

Ya hemos planteado preocupaciones y equivalen a "esto no debería estar en el idioma". Esas preocupaciones no van a desaparecer.

Para ser claros, me refería a registrarlos con @rfcbot porque el bot no verá las viejas inquietudes registradas después de que se haya cancelado una propuesta. Le planteé esa inquietud porque sé que tiene inquietudes, como cortesía hacia usted.

Prefiero la noción de nrc de que las macros deberían poder generar IR. Eso parece una solución completamente razonable para esto y muchas posibilidades futuras, sin necesariamente agregar a la sintaxis superficial del lenguaje.

No tengo idea de cómo se vería o de que es una mejor idea; parece bastante especulativo (en el sentido de "esto podría llevar años antes de que se acuerde un diseño") y más costoso que la solución de bajo costo de break 'label expr que también tiene beneficios más allá de ser el resultado de macro expansión.

_Como nota de proceso para este tema y para todos los demás, evite cancelar propuestas de un lado a otro ... si cree que algo no debería avanzar, solo use las preocupaciones para eso.

Ciertamente estoy de acuerdo con eso. Sin embargo, no creo que una preocupación sea el mecanismo apropiado aquí. Una inquietud parece ser "si esto se resolviera, lo aprobaría". Aquí, el problema es "esto no debería avanzar en absoluto, 'fusionar' es el objetivo incorrecto, me gustaría 'cerrar' en su lugar". Eso no parece manejarse mejor con el mecanismo de una "preocupación".

@joshtriplett Como punto de interés, ¿podría dirigirme a las inquietudes que se plantearon que mencionó anteriormente? He revisado el hilo de RFC original y no estoy seguro de a qué se refiere específicamente. Gracias.

@rfcbot se refiere a la ergonomía y la optimización / rendimiento (ejemplo sin óxido de rendimiento y ergonomía reducidos https://gist.github.com/SoniEx2/fc5d3614614e4e3fe131#file-special-lua)

No sé si estoy usando esto correctamente

(tenga en cuenta que algunos de los "desugaring" (en realidad, yo diría que es todo lo contrario de desugaring - goto es un primitivo, bucles desugar en goto) es bastante horrible)

(también tenga en cuenta que lua no tiene nada etiquetado, por lo que le obliga a usar goto. Incluso entonces, creo que al menos se necesitaría un bloque de ruptura, pero probablemente con un "desugaring" más limpio).

La preocupación de

(Como lo sugirió @centril en Discord).

No estoy seguro de si este es un argumento a favor o en contra de la estabilización, pero tenga en cuenta que es trivialmente fácil de codificar en Rust estable hoy:

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

Así que, por un lado, deberíamos estabilizar esto porque apenas está creciendo el lenguaje, simplemente eliminando for _ in 0..1 , por otro lado, no deberíamos estabilizar esto porque hay una manera fácil de hacerlo hoy (para cuando es realmente necesario) y no deberíamos alentar el uso de tal anti-patrón.

parece un poco confuso y la etiqueta es innecesaria.

romper bloques es mucho menos confuso y requiere la etiqueta.

No entiendo por qué piensas que romper bloques es un anti-patrón. lo que se aplica a X no se aplica necesariamente a no-exactamente-X. Es posible que pueda comprar una computadora por $ 299,99, pero no por $ 299,98, aunque sean "básicamente" iguales.

@nrc

No estoy seguro de si este es un argumento a favor o en contra de la estabilización, pero tenga en cuenta que es trivialmente fácil de codificar en Rust estable hoy:

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

Entonces, por un lado, deberíamos estabilizar esto porque apenas está creciendo el lenguaje, simplemente eliminando for _ in 0..1 ,

'a: for _ in 0..1 { break 'a } podría funcionar si el objetivo explícito es que nadie escriba nunca 'a: { ... break 'a e; ... } ; sin embargo, tiene el inconveniente de generar IR basura que el verificador de tipos, MIR y LLVM deben procesar, lo que empeora los tiempos de compilación (al menos en comparación con LBV).

por otro lado, no deberíamos estabilizar esto porque hay una manera fácil de hacerlo hoy (para cuando sea realmente necesario) y no deberíamos fomentar que se utilice tal anti-patrón.

Creo que no estamos de acuerdo en que LBV sea un anti-patrón. Al final del día, optamos por imbuir a Rust con un flujo de control imperativo en lugar de que Rust sea un lenguaje de programación funcional. Si bien podría haber preferido no tener ese flujo de control, es un hecho consumado. La pregunta es entonces si LBV es de alguna manera más ilegible o problemático que otros mecanismos imperativos en Rust. No creo que lo sea.

Si bien creo que las funciones deben mantenerse cortas (verticalmente) y poco profundas (sangría), es mejor ser largas y superficiales que largas y profundas. Encuentro que LBV ayuda a evitar la profundidad en algunos de los flujos más complicados. LBV también me parece legible, ya que denota explícitamente a dónde saltará, haciendo que el flujo se entienda bien. Con todo, encuentro que LBV es uno de los flujos de control imperativo menos problemáticos.

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

pueden parecer similares, pero no son exactamente iguales. mientras que el primero es posiblemente un anti-patrón, el segundo posiblemente no lo sea.

algo así como loop {} vs while true {} (concedido loop puede devolver un valor mientras que while no puede, pero ignoremos eso por un momento)

@nrc Sin embargo, eso no tiene el mismo comportamiento: https://github.com/rust-lang/rust/issues/48594#issuecomment -450246249

En particular, el comportamiento de break; y continue; es diferente. Considere este código hipotético:

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

Si some_macro expande a 'a { ... } , eso tiene un comportamiento diferente que si se expande a 'a: for _ 0..1 { ... }

@ SoniEx2

¿Alguien ha intentado reescribir las cosas de una manera más lenta, menos intuitiva y menos ergonómica?

Por favor, intente comunicar sus preocupaciones de una manera más positiva y productiva. Burlarse de los demás no nos lleva a ninguna parte y hace que la gente se sienta mal. Todos estamos aquí porque queremos asegurarnos de que Rust sea el mejor lenguaje de programación posible, y parte de ese proceso es garantizar que la comunidad sea lo más positiva y alentadora posible.

Quizás estoy cometiendo un gran malentendido. No pretendo ser un experto en LLVM, los componentes internos de Rust o cosas similares. Dicho esto, tengo alguna experiencia rudimentaria con el diseño de compiladores y estoy confundido en cuanto a cuál es la preocupación. Si alguien pudiera explicar las cosas, realmente lo agradecería.

La forma en que veo las cosas:

1) Esto no cambia la ergonomía del control de flujo. Ya existen construcciones como esta en Rust como 'a: loop { if x { break 'a; } break; } .

2) Esto no cambia particularmente la ergonomía de los retornos de bloque: los bucles ya son capaces de devolver valores.

Para mí, esto se parece más a la finalización intuitiva de un conjunto de características que Rust ya tiene: generalizar esas características para todos (o al menos más) bloques.

En términos de preocupaciones acerca de que esto sea demasiado similar a goto , estoy aún más confundido. Esto no agrega nada que no sea posible en Rust, y no permite saltos hacia atrás (el problema principal que resulta en un mal uso de goto regresando al código espagueti), así que estoy fallando para comprender qué ramificaciones podría tener esta característica en codegen, ya que parece ser solo azúcar de sintaxis para un bucle siempre en ruptura.

¿Alguien podría explicarme en términos más específicos qué problemas existen?

Desde el punto de vista del procedimiento, en mi opinión, no es apropiado presentar inquietudes de estilo concern blocking o concern should-close-not-merge : esta inquietud no enumera un problema con la función propuesta ni nada en lo que podamos trabajar para resolver- - es solo un bloqueador permanente. No creo que el mecanismo de inquietudes de rfcbot deba usarse para bloquear permanentemente las funciones, sino solo para ayudar a rastrear la resolución de inquietudes y asegurarse de que se aborden / resuelvan de manera adecuada. Mi entendimiento del proceso era que la intención era usar una casilla de verificación sin marcar para marcar su desacuerdo, y que las inquietudes debían usarse para presentar problemas específicos y discutibles sobre la función y su funcionalidad.

Podría haber presentado preocupaciones similares de "No me gusta esto" sobre otras características con las que personalmente no estaba de acuerdo (ver, por ejemplo, uniform_paths), pero no creo que el filibustero infinito sea una forma productiva de hacer diseño de lenguaje.

Si hay inquietudes específicas sobre la forma en que esta característica interactúa con otras partes del lenguaje que deberían discutirse / resolverse (por ejemplo, "esto parece que podría obviarse con try { ... } " o "ayuda a habilitar la funciones idiomáticamente grandes ") Creo que sería más productivo archivarlas de esa manera.

@cramertj No habría presentado tal inquietud de esa forma si @Centril no lo hubiera sugerido explícitamente. (Personalmente, hubiera preferido que no se hubiera propuesto el FCP).

¿Qué le sugeriría que el proceso adecuado para "esto debe ser cerrado", si alguien ha presentado una P-FCP con rfcbot por algo distinto de "cerca"? "asegúrese de que se abordan / resuelven adecuadamente" suena equivalente a "esto se estabilizará algún día, ¿qué se necesita para llegar allí?". Eso no deja un camino en el flujo para "esto nunca debe estabilizarse, esto no debe ser parte del lenguaje".

Mi entendimiento del proceso fue que la intención era usar una casilla de verificación sin marcar para marcar su desacuerdo

Luego, tendríamos que volver a cambiar el proceso para que requiera que se marquen todas las casillas de verificación para continuar.

No creo que el filibustero infinito sea una forma productiva de hacer diseño de lenguaje.

Yo tampoco creo que lo sea. También me gustaría encontrar un camino para concluir esto, solo me gustaría verlo concluido en una dirección diferente.

Para que conste, según las experiencias con este RFC, no creo que sea una buena idea volver a responder a un RFC con una advertencia de "podemos evaluar / abordar durante la estabilización si debemos proceder", porque me parece claro que hacerlo produce problemas de procedimiento críticos como este.

Necesitamos una ruta para decir "sí, puedo ver cómo esta característica individual haría que el idioma sea más expresivo, pero según la evaluación general del idioma, no tiene su peso y no quiero expandir más el superficie de la lengua de esta manera ". Creo que debemos hacer esa llamada con regularidad, no sea que cada característica propuesta se considere inevitable de alguna forma y solo sea una cuestión de sofocar objeciones y persistir.

Repetiré lo que dije antes porque parece que se ha perdido: https://github.com/rust-lang/rust/issues/48594#issuecomment -451795597

Básicamente hablé sobre las diferencias entre usar un bucle y usar un bloque de ruptura.

principalmente, los bloques de ruptura tienen una sintaxis diferente, una semántica (ligeramente) diferente (con respecto a la ruptura sin etiquetar como mínimo), significan una intención diferente y algunas otras cosas menores.

¿rfcbot soporta anticoncerns?

@joshtriplett

Necesitamos una ruta para decir "sí, puedo ver cómo esta característica individual haría que el idioma sea más expresivo, pero según la evaluación general del idioma, no tiene su peso y no quiero expandir más el superficie de la lengua de esta manera ". Creo que debemos hacer esa llamada con regularidad, no sea que cada característica propuesta se considere inevitable de alguna forma y solo sea una cuestión de sofocar objeciones y persistir.

Sí, es un peligro desafortunado de nuestro proceso actual que aceptar o rechazar una función requiere el consenso total del equipo apropiado. Cuando el equipo de lang es tan grande como lo es ahora, es difícil lograrlo siempre. Por los comentarios anteriores, pensé que solo faltaban ejemplos de código para motivar por qué esta función es importante, pero ahora parece que estás de acuerdo en que hay es un código que se puede escribir mejor con esta función, pero no está convencido de que valga la pena los costos que ve aquí. No estoy seguro de una manera de convencerlo de que estos casos son una motivación suficiente, ya que parece que continuar brindando ejemplos (incluido el ejemplo macro, que es literalmente imposible de escribir en otro estilo) no es suficiente.

De manera similar, estoy bastante seguro de que, personalmente, seguiré convencido de que esta función debería fusionarse: en mi opinión, no solo aplica su peso a través de una variedad de ejemplos en los que es la mejor / única opción, sino que el lenguaje es en realidad más simple con su adición. (ya que no permitir bloques etiquetados me sorprende dado que permitimos otras construcciones etiquetadas).

Si está de acuerdo con mi resumen anterior de su posición, entonces parece que estamos en un desafortunado (y, creo, histórico!) Impass sin un proceso. Una opción es interpretar las reglas actuales de rfcbot como hice anteriormente en el sentido de que "ninguna casilla de verificación indica su desacuerdo, son necesarios tres miembros para anular a la mayoría", pero no creo que sea así como se entendieron las reglas cuando se introdujeron, así que Creo que fue poco sincero por mi parte sugerir que debería seguir este procedimiento (aunque lo hice yo mismo en otro lugar).

Anteriormente escuché sugerencias de que podríamos introducir un límite de tiempo para las funciones que van de aprobadas-> implementadas-> estabilizadas, y que deberíamos "cerrar automáticamente" las funciones que se retrasan en un esfuerzo por evitar un atasco cada vez mayor. Algo así podría abordar este caso, en el que yo (y, creo, varios otros en el equipo) no marcaré casillas de verificación para cerrar, ni tú marcarás una casilla de verificación para aceptar (todavía tengo miedo incluso ahora de decir esto que yo ' ¡Estoy haciendo un último esfuerzo para convencerte !: sonríe :).

Me preocupa que con equipos en constante crecimiento perdamos la capacidad de llegar a un consenso sobre las características, y que será difícil dirigir el lenguaje de manera coherente, particularmente en el equipo de diseño de lenguaje. Los límites de tamaño del equipo parecen una solución obvia a esto: es posible recibir información de un gran número de personas, pero es bastante imposible construir un consenso absoluto entre un grupo suficientemente grande. Otros probablemente argumentarán que las características contenciosas no deben fusionarse en un esfuerzo por salvaguardar el lenguaje de errores. Personalmente, creo que es poco probable que la comunidad nos permita cometer muchos de esos errores sin una advertencia adecuada, pero es una idea.

Intentaré comenzar un hilo separado para discutir la evolución del proceso aquí, y mientras tanto, les pediría a las personas que participan en este hilo que publiquen solo si hay nuevos casos de uso críticos para considerar que sean notablemente diferentes de los anteriores, o si hay una razón importante no considerada por la cual esta característica no debe agregarse al idioma. Leer toda la historia en estos megahebras es difícil, pero lo es aún más cuando las publicaciones se repiten una y otra vez, o cuando el hilo está lleno de comentarios inútiles. (dice haber escrito ahora uno de los comentarios más largos de todo el hilo XD)

TL; DR: Creo que estamos atascados, deberíamos tener un proceso para esto, comenzaré esa conversación en otro lugar y la vincularé aquí. De lo contrario, no comente a menos que tenga información nueva importante que deba tenerse en cuenta.

@cramertj

Es un peligro desafortunado de nuestro proceso actual que aceptar o rechazar una función requiere el consenso total del equipo apropiado.

Sinceramente, lo consideraría una característica.

Por los comentarios anteriores, pensé que solo faltaban ejemplos de código para motivar por qué esta función es importante, pero ahora parece que estás de acuerdo en que hay un código que se puede escribir mejor con esta función, pero no estás convencido de que valga la pena. los costos que ves aquí.

Sigo sintiendo que muchos de los ejemplos podrían escribirse de otras formas. Esto no añade ninguna expresividad irrepresentable al lenguaje. Originalmente sentí que podría estar motivado con suficientes ejemplos, pero cuantos más ejemplos veo que podrían usar esta función, más estoy de acuerdo con @withoutboats en que esta función simplemente no debería

(También vale la pena señalar: la caja de @nrc proc-macro-rules que usaba label_break_value parece haber sido reescrita para evitarla).

Una opción es interpretar las reglas actuales de rfcbot como hice anteriormente en el sentido de que "ninguna casilla de verificación indica su desacuerdo, son necesarios tres miembros para anular a la mayoría", pero no creo que sea así como se entendieron las reglas cuando se introdujeron, así que Creo que fue poco sincero por mi parte sugerir que debería seguir este procedimiento (aunque lo hice yo mismo en otro lugar).

Creo que ese es el procedimiento correcto para "abstenerse", pero no para objetar.

En mi opinión, no solo aplica su peso a través de una variedad de ejemplos en los que es la mejor / única opción, sino que el lenguaje es en realidad más simple con su adición (ya que no permitir bloques etiquetados me sorprende dado que permitimos otras construcciones etiquetadas).

Por lo general, encuentro convincentes los argumentos de ortogonalidad, pero este en particular lo encuentro poco convincente, ya que personalmente hubiera preferido no tener roturas de bucle etiquetadas en el lenguaje tampoco.

Me preocupa que con equipos en constante crecimiento perdamos la capacidad de llegar a un consenso sobre las características, y que será difícil dirigir el lenguaje de manera coherente, particularmente en el equipo de diseño de lenguaje.

No solo me preocupo por la dirección, sino también por detenerme . Hay una cierta inevitabilidad que surge a veces, donde el proceso parece centrado en encontrar un camino hacia el "sí" y no hay un borde de gráfico en el diagrama de flujo que lleve a "no", simplemente "todavía no".

Se han escrito muchas publicaciones en 2018 y 2019 que hablan sobre el crecimiento de las características en el idioma, y ​​creo que en el equipo de idiomas debemos considerar seriamente la superficie total del idioma. Y siento que no tenemos buenos procesos que nos animen a hacer eso.

@joshtriplett

Esto no añade ninguna expresividad irrepresentable al lenguaje.

Para ser muy claro: hace exactamente esto. Actualmente no hay forma de expresar este código que no interfiera con otras construcciones de flujo de control, lo cual (como otros han señalado) es especialmente deseable en macros, donde esta sería la única construcción que no se puede dirigir a través de rupturas no etiquetadas, permitiendo a los macroautores separarse de las secciones sin arriesgarse a una superposición con un break proporcionado por el usuario.

también hace que el código sea más legible ya que no tienes que andar en zig-zag (si no tuviéramos etiquetas, mira el ejemplo de Lua) o hacer cosas raras con bucles. esto también tiene un pequeño beneficio de rendimiento, aunque llvm debería poder optimizar el código en zigzag.

@joshtriplett

Sigo pensando que muchos de los ejemplos _podrían_ escribirse de otras formas. Esto no añade ninguna expresividad irrepresentable al lenguaje. Originalmente sentí que podría estar motivado con suficientes ejemplos, pero cuantos más ejemplos veo que _podría_ usar esta función, más estoy de acuerdo con incluirse en el idioma.

¿Es eso algo en lo que quizás podamos profundizar?

Por lo general, encuentro convincentes los argumentos de ortogonalidad, pero este en particular lo encuentro poco convincente, ya que personalmente hubiera preferido no tener roturas de bucle etiquetadas en el lenguaje tampoco.

Esta línea de razonamiento me parece extraña (a menos que desee eliminar los saltos de bucle etiquetados con una edición). No parece apropiado basar las decisiones de diseño en lo que desearía que no estuviera en el idioma. Está ahí, por lo que deberíamos considerar si esta adición es coherente con eso. De lo contrario, hay muchas cosas que podría haber hecho de manera diferente con respecto a Rust, pero no debería ni puedo.

No solo me preocupo por la dirección, sino también por detenerme. Hay una cierta inevitabilidad que surge a veces, donde el proceso parece centrado en encontrar un camino hacia el "sí" y no hay un borde de gráfico en el diagrama de flujo que lleve a "no", simplemente "todavía no".

Aún no es su propia forma de "no" en el sentido de que no se estabilizará sin un sí. Además, hay un no: convéncenos al resto de nosotros de que LBV es una mala idea / no está lo suficientemente motivado por razones X, Y y Z. Todavía tengo que escuchar muchos de esos argumentos concretos. También ha demostrado claramente aquí que no hay inevitabilidad.

Se han escrito muchas publicaciones en 2018 y 2019 que hablan sobre el crecimiento de las características en el idioma, y ​​creo que en el equipo de idiomas debemos considerar _ seriamente_ la superficie total del idioma. Y siento que no tenemos buenos procesos que nos animen a hacer eso.

Personalmente, creo que muchos de estos mensajes son o bien sobre la sostenibilidad (pero no tan acertadamente tenor @nikomatsakis hicieron ...) o que muchas personas no entienden cómo el equipo funciona lenguaje (es decir, que ya hacemos considerar seriamente al total área de superficie). La sintaxis de superficie total de Rust probablemente se ha reducido durante el último año, no ha aumentado. LBV no aumenta eso notablemente y se podría argumentar que en realidad reduce el número de producciones en el lenguaje y hace que la sintaxis sea más uniforme.

La mera combinación de producciones gramaticales no reduce la superficie del idioma.

Reducir el número de ramas condicionales que se pueden tomar es posiblemente reducir la superficie del lenguaje.

No creo que esta característica en particular sea de ninguna manera, como tal, probablemente sea neutral. Pero por ejemplo, cosas que las construcciones del lenguaje unificar (Bool permiten, alguien?) Hacen encoger superficie.

Por lo que vale, Common Lisp, que es similar a Rust en el sentido de que es un lenguaje de múltiples paradigmas con macros, tiene esta característica (llamada block / return-from ). De manera similar a lo que se ha discutido aquí, en Common Lisp esta construcción es a menudo útil cuando se escriben macros y se expresa un flujo de control irreductiblemente complejo.

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

Mi sensación es que en Common Lisp esta función se considera exitosa. No surge en conversaciones sobre características que hacen que el idioma sea difícil de aprender o implementar.

Hay la inclusión

early return from block ⊂ exceptions ⊂ call/cc

Por ejemplo, en Scheme, la siguiente emulación de return-from es factible:

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

En Scheme, call/cc se considera tan exitoso como controvertido. Encuentro esto particularmente interesante porque primero tienes que formular un objeto para poder hablar de él. Incluso si considera call/cc como un error, hizo que el lenguaje fuera más sustancial en un sentido intelectual.

El lenguaje bloques con
Es muy útil y conveniente.

return, break y continue se implementan como macros usando esta función.

@jhpratt Lea la discusión en el tema.

Repitiendo algunos comentarios que hice sobre IRLO por sugerencia de @Centril :

Al discutir esta característica, sugerí la siguiente inspiración (en contra de la versión habitual relacionada con el ciclo):

Los bucles no son realmente mi inspiración aquí, de todos modos. Cuando miro los bloques de prueba, creo que "hombre, sería útil poder escribir algunos cálculos locales breves con retornos tempranos sin tener que tomarse la molestia de crear un cierre". Los bloques de prueba funcionan para el bastante restringido "Quiero devolver un intento implícito", y no admiten ningún tipo de etiquetas.

El anterior es mi caso de uso principal para esto: me gusta el estilo de retorno temprano. try {} bloques try {}? para bloques anidados) y harás calza su tipo en una cierta forma monádica que no es un catchall (como lo demuestran los casos de uso proporcionados).

También hice algunas observaciones sobre C ++, donde no tener rotura / continuación de etiquetas es muy doloroso, especialmente en ausencia de iteradores similares a Rust. Por ejemplo, no hay forma de continuar un bucle externo desde un bucle interno sin un posible UB goto o un break local más un continue fuera del bucle interno.

Al menos, la falta de un verificador de préstamos de C ++ hace que el truco de lambda en línea sea menos doloroso, lo que , efectivamente, soporta LVB, ya que regresar en el lambda en línea se convierte en algo como LVB por el inliner de LLVM. Algo como esto es ... bastante más cuestionable en Rust.

También debo señalar que esta característica es aproximadamente equivalente en expresividad a goto Go, que en tiempo de compilación obliga a no saltar declaraciones y demás (francamente, si Rob Pike pensara que goto era aceptable, dado la historia de tratar de resolver los problemas de C ++, confiaría un poco en él).

Además, si vamos a profundizar en la técnica anterior, Kotlin también proporciona esta característica exacta, en forma de return<strong i="24">@label</strong> expr; .

En los lenguajes c'ish, a menudo uso etiquetas, incluso sin ningún goto entrante, como ubicaciones para puntos de interrupción estables en bucles, porque el depurador puede reconocer break function:label .
Incluso sin un consenso sobre la ruptura, las etiquetas podrían ser agradables.

Editar: Un obstáculo potencial es que, por lo general, las etiquetas siguen la convención de nomenclatura de símbolos, si entiendo el RFC, estas etiquetas no siguen la convención de nomenclatura de símbolos donde ', no es válido en un nombre de símbolo.
Sin embargo, no estoy seguro de antemano en, por ejemplo, Dwarf o gdb si de hecho hay algún problema aquí.

Edición 2:
Estoy bastante seguro de que hay algo de humo en esto, si observamos el comportamiento de las comillas de las etiquetas normales basadas en c,
en el depurador, gdb al menos tratará las comillas, para citar en lugar de parte del nombre del símbolo. Los siguientes resultados en

Punto de interrupción 1 en 0x401114: archivo, línea 1.
cita inigualable

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

Y no creo que esto pueda verse afectado por el soporte específico del lenguaje rust en gdb, ya que creo que esta cita ocurre antes de la coincidencia de símbolos.

Editar: El barco probablemente navegó en esto debido a las etiquetas de bucle existentes.

Un punto menor a favor del retorno temprano de los bloques sería la programación de contratos de los pobres. Uno simplemente agrega afirmaciones como condiciones previas y posteriores. Para mantener la comodidad, debería ser posible reemplazar return con break 'ret para permitir esta construcción:

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

Sin embargo, esta es una solución imperfecta, porque return debería estar prohibido dentro del bloque.

Añadiendo una nota de que quería esta función pero no la usé porque no sabía que existía, lo que creo que es un modificador negativo en "la gente no la quiere, como lo demuestra la poca gente que la usa".

Esta mañana reinventé de forma independiente

He estado jugando con esto hoy, y es _super_ útil en bloques async . Sin embargo, parece tener problemas cuando se combina con la función 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

Los bloques de prueba son un error.

... ¿no puedes etiquetar el ? sí mismo? (como en Err(()) 'foo?; )

Estoy totalmente en desacuerdo con que los bloques de prueba sean un error, aunque esa es una discusión separada, y probablemente no valga la pena ir y venir aquí.

En este ejemplo en particular, podría ser factible, pero esto está muy minimizado en comparación con el código real que tengo, donde 'foo contiene un fragmento de código decente y varios ? s.

@ SoniEx2

Los bloques de prueba son un error.

Este comentario es inapropiado. El comentario de @jonhoo informaba sobre una interacción (presumiblemente) con errores. Independientemente de las opiniones de uno sobre los bloques try (o label-break-value), está claro que deberían interoperar sin problemas.

deberían hacerlo, con la sintaxis Err(()) 'foo?; .

@jonhoo Sospecho que está viendo que se filtran detalles implícitos en términos de cómo try se desaprueba; ¿puede presentarlo como un problema separado y podemos mover la discusión de posibles soluciones allí?

El RFC dice que

'BLOCK_LABEL: { EXPR }

es azúcar sintáctico para

'BLOCK_LABEL: loop { break { EXPR } }

Intenté hacer esa sustitución y el código se compila con una advertencia sobre el código inalcanzable.

#![feature(try_blocks, label_break_value)]

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

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

Encuentro que ya no me opongo a esto. Me hubiera opuesto con más fuerza al concepto inicial de rotura etiquetada si estuviera en consideración hoy, pero dado que ese concepto existe, no creo que tenga sentido para mí continuar objetando su aplicación a bloques arbitrarios.

(Esto se aplica al formulario actual, usando break , no a ninguna otra sintaxis).

@rfcbot resolver debería-cerrar-no-fusionar
@rfcbot revisado

@joshtriplett Por lo que vale, he descubierto que esto es inmensamente útil en los bloques async , ya que es la única forma de hacer un "retorno anticipado". Significa que en lugar de escribir:

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

Puedo escribir:

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

Esto es claramente análogo a cómo puede regresar anticipadamente con return en funciones / cierres, y con continue/break en bucles. Es cierto que sería bueno si no necesitara el bloque adicional ( async 'block { una posibilidad?), Pero definitivamente supera a los if-s anidados.

Permitir que los bloques asincrónicos se anoten directamente con etiquetas suena como una muy buena extensión de esta función.

@rfcbot fcp cancelar

Voy a cancelar el FCP aquí porque ha estado bloqueado para siempre. Probablemente deberíamos discutir si queremos impulsar este futuro. Al menos, parece que debería actualizarse para tener en cuenta los bloques asincrónicos, y parece que eso agrega un nuevo caso de uso para la función.

Propuesta de @nikomatsakis cancelada.

Tenga en cuenta que no hay ambigüedad sobre la semántica de esta propuesta en presencia de bloques asíncronos: la definición en el RFC todavía se aplica, es decir,

'BLOCK_LABEL: { EXPR }

es simplemente azúcar sintáctico para

'BLOCK_LABEL: loop { break { EXPR } }

excepto que dentro del EXPR se prohíben las interrupciones o continuas sin etiquetar que se unirían al bucle implícito.

Tenga en cuenta que puede (temprano) return de bloques asíncronos, en lugar de etiquetarlos como break , por lo que etiquetar bloques asíncronos no tiene mucho sentido:

let fut = async {
    return 42;
    0
};

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

( patio de recreo )

@WaffleLapkin ¡De hecho, vine aquí para notar eso, ya que yo mismo me informaron recientemente de eso ! Creo que la función sigue siendo muy útil para poder _saltar_ secciones de código (no ejecute el resto de este bloque, pero tampoco regrese), pero su aplicabilidad para async _específicamente_ es menor que Pensé inicialmente.

¿Fue útil esta página
0 / 5 - 0 calificaciones