Rust: Problema de seguimiento para genéricos const (RFC 2000)

Creado en 15 sept. 2017  ·  202Comentarios  ·  Fuente: rust-lang/rust

Problema de seguimiento para rust-lang / rfcs # 2000

Actualizaciones:

Si desea ayudar, eche un vistazo a los problemas de genéricos const abiertos y no dude en hacer ping a @varkor , @eddyb , @yodaldevoid , @ oli-obk o @lcnr para obtener ayuda para comenzar.


Estabilización de bloqueo:

  • [ ] Diseño:

    • [] Resolviendo el orden de los parámetros de tipo y const, con los parámetros predeterminados

    • [] Decida cuál es el mejor equilibrio entre costos de implementación y UX para unificar expresiones de const abstractas.

    • [] Cómo determinamos la forma correcta de las expresiones constantes.

  • [x] Implementación
  • [] Documentación

    • [] guía rustc


Problemas de implementación restantes:

  • [] Resuelve varios FIXME(const_generics) comentarios.
  • [] Resolver inquietudes con canonicalización / normalización perezosa.
  • [] Investigar el manejo de parámetros const en patrones.
  • [] Agregue más pruebas.
  • [] Implementar valores predeterminados para parámetros const ( FIXME(const_generics:defaults) ).
  • [] Solucione otros problemas de genéricos A-const .
  • [] Audite los usos de has_infer_types .
  • [x] Prohibir expresiones complejas para argumentos constantes que involucren parámetros (por ahora), por ejemplo, {X * 2} .
  • [] Diagnóstico de auditoría (por ejemplo, https://github.com/rust-lang/rust/pull/76401#discussion_r484819320).
A-const-fn A-const-generics A-typesystem B-RFC-approved C-tracking-issue F-const_generics T-compiler T-lang requires-nightly

Comentario más útil

Aquí hay un resumen del progreso hasta ahora en genéricos const.


Antes de que el trabajo sobre los genéricos const pudiera comenzar correctamente, era necesario realizar una refactorización. @jplatte asumió la tarea con https://github.com/rust-lang/rust/pull/45930. @jplatte luego comenzó a trabajar en la implementación principal de genéricos const, pero descubrió que no tenían tiempo suficiente para continuar.

@yodaldevoid y yo continuamos donde lo dejó https://github.com/rust-lang/rust/pull/48149 , https://github.com/rust-lang/rust/pull / 48452 , https://github.com/rust-lang/rust/pull/48523 , https://github.com/rust-lang/rust/pull/51880.

Una vez hecho esto, la implementación de genéricos const podría comenzar en serio. Desde entonces, @yodaldevoid y yo hemos ido agregando gradualmente soporte para genéricos const: https://github.com/rust-lang/rust/pull/58191 , https://github.com/rust-lang/rust / pull / 58503 , https://github.com/rust-lang/rust/pull/58581 , https://github.com/rust-lang/rust/pull/58583 , https://github.com/rust -lang / rust / pull / 59170 , https://github.com/rust-lang/rust/pull/59355 , https://github.com/rust-lang/rust/pull/59415 , https: // github .com / rust-lang / rust / pull / 60058 , https://github.com/rust-lang/rust/pull/60280 , https://github.com/rust-lang/rust/pull/60284 y la mayoría recientemente https://github.com/rust-lang/rust/pull/59008. (Estos en su mayoría se han separado de la solicitud de extracción de genéricos const principal ).


¿Cuál es el estado ahora? Algunas pruebas genéricas const ahora funcionan 🎉Sin embargo, todavía hay algunas que no lo hacen, y hay una serie de FIXME s en todo el código que aún tenemos que abordar. Sin embargo, ahora nos estamos acercando, y estamos en la etapa en la que hay algo de fruta madura si quieres ayudar.

  • Primero, necesitamos más pruebas. Hasta ahora solo hay un puñado de pruebas genéricas const , pero nos gustaría muchas más. Por el momento, debido a que somos conscientes de una serie de problemas, no necesitamos informes de errores, pero sí necesitamos pasar las pruebas. Si se le ocurre un caso de prueba que funciona (y no se parece demasiado a una prueba existente), no dude en abrir una solicitud de extracción para agregarlo.
  • En segundo lugar, como se mencionó, hay una serie de FIXME(const_generics) repartidos por todo el código. Estamos planeando trabajar en nuestro camino a través de ellos, pero @yodaldevoid y yo solo tenemos mucho tiempo, así que si crees que puedes abordar uno, adelante (aunque es posible que quieras dejar un comentario que diga lo mismo, así que no lo hacemos. t duplicar esfuerzos).

Escribí una descripción general de algunos de los problemas de implementación restantes antes de que los genéricos const estén listos para las pruebas adecuadas, en la publicación superior, para dar una idea aproximada de lo que queda por hacer.

Ha llevado tiempo, pero hemos estado progresando constantemente y esperamos mantener el ritmo. ¡Es motivador ver finalmente que las cosas comienzan a encajar!

Todos 202 comentarios

44275 agregó un predicado ConstEvaluatable , y WF([T; expr]) ahora requiere ConstEvaluatable(expr) , ya que expr se evalúa perezosamente (incluso si es solo un literal entero). El cumplimiento de este predicado requiere que la expresión se evalúe correctamente, mientras que la normalización ignora el error y simplemente deja intacta la expresión Unevaluated que encontró, que es más o menos lo que sucede con las proyecciones de tipo asociadas. Espero que el mismo sistema se adapte a los genéricos constantes.

@EpicatSupercell ha expresado interés en trabajar en esto, los

Es decir, necesitamos la normalización perezosa de @nikomatsakis para permitir que las expresiones constantes incrustadas en los tipos observen los límites en el alcance (desde la función / definición de tipo / impl / etc.el elemento en el que se encuentran), sin producir dependencias cíclicas la mitad del tiempo.

Puntos de referencia de implementación (para una tutoría más directa, busque @eddyb en Gitter o eddyb en IRC):

  • Declaración / Sintaxis:
  • Declaración / Semántica

    • Estructuras de datos: ty::Generics - agregue const parámetros junto con los de tipo

    • Conversión de HIR: generics_of - crear ty::ConstParameterDef partir de hir::ConstParam

  • Resolución de uso / nombre

    • Estructuras de datos: Def - agregue una variante para los parámetros const

    • Pase de resolución de nombre: with_type_parameter_rib - admite tanto tipos como const genéricos

  • Uso / Semántica
  • Inferencia

Tenga en cuenta que todo esto debería permitir impl<T, const N: usize> Trait for [T; N] {...} , pero no pasar una expresión constante a un tipo / función, por ejemplo, ArrayVec<T, 3> .

Me gustaría probar esto :)

@jplatte @eddyb ¿ Alguna noticia sobre esto?

@samsartor hubo una refactorización que se tuvo que hacer antes del trabajo de implementación principal. Eso ya está casi hecho (estoy esperando comentarios actualmente). En realidad, no sé cuánto trabajo hay después de eso. Analizar const params fue con lo que comencé inicialmente, antes de la refactorización. No está hecho, pero debería poder hacerlo relativamente pronto.

@jplatte Sería bueno si pudiera vincular la solicitud de extracción. No pude encontrarlo.

Aún no hay relaciones públicas. Todo mi trabajo se puede encontrar aquí .

Buen trabajo hasta ahora @jplatte 🍻

Ahora hay un PR para el trabajo de base ( Generics refactorización): # 45930

Creo que esto ya está incluido, pero sería bueno manejar el plegado estilo C ++ para manejar matrices dimensionales de estilo n de álgebra lineal con longitudes variables, es decir, una matriz dimensional 4x4 [[f64; 4]; 4] o 4x3x5x6 [[[[ f64; 6]; 5]; 3]; 4] y ser capaz de envolver y generar adecuadamente métodos especializados tanto para él como para implementaciones de rasgos adecuadas para escalares, vectores correctamente dimensionados, etc. Ver https://gist.github.com/huhlig / 8b21850b54a75254be4b093551f8c2cb para un ejemplo básico.

No recuerdo a nadie que haya propuesto expresiones plegables para Rust antes, y mucho menos como parte de este RFC. Pero dado que esto es Rust, ¿hay alguna razón por la que no podamos implementar expresiones de plegado como una macro ordinaria, en lugar de una nueva sintaxis dedicada?

¿Cuáles son los próximos pasos ahora que # 45930 se fusionó? @jplatte

@kjaleshire El siguiente paso es implementar el análisis de los genéricos const (ver el comentario de @eddyb más arriba). Ya comencé a trabajar en esto antes de que quedara claro que la refactorización en la refactorización sería necesaria. Mi trabajo existente sobre eso se puede encontrar aquí ; Sin embargo, aún no se ha vuelto a basar desde que se fusionó el PR de refactorización.

@jplatte Creo que querías mencionar a @kjetilkjeka.

¡Gracias por la actualización! Estoy seguro de que no soy el único que espera esta función. ¡Sigan con el buen trabajo!

@jplatte no quiere estar impaciente, pero ¿ha habido algún trabajo en esto? ¿Necesitas ayuda? ¿Alguien debería ayudar?

@ est31 Lo siento. No he tenido tiempo para trabajar en esto en un tiempo, y no estoy del todo seguro de cuándo tendré tiempo. Tal vez sea mejor que alguien más continúe donde lo dejé. Creo que la parte de análisis ya está terminada. Hay dos lugares en el código donde no estaba seguro de qué hacer en el caso de que un parámetro genérico fuera un parámetro constante:

Tampoco hay pruebas para el análisis de genéricos const todavía (y para el código de impresión bonita que actualicé al mismo tiempo). No estoy seguro de qué otra información puedo proporcionar que sea necesaria / útil para que otra persona continúe trabajando en esto, pero no dude en enviarme un ping si algo no está claro sobre mi código.

@jplatte ~ None en el primero y nada en el segundo ~~ (cc @pnkfelix @nikomatsakis)

EDITAR : nada que hacer en cualquier caso.

@eddyb Para el primer fragmento vinculado, ¿está seguro de que se debe devolver None ? Puede que no entienda lo que está pasando aquí, pero me parece que no se debe hacer nada. Si se devuelve None , se omitirán otros parámetros que podrían no ser seguros.

@yodaldevoid Lo siento, tienes razón, no me di cuenta de que esto estaba en Generics .

Me gustaría retomar esto donde lo dejó siguiendo las instrucciones de tutoría de

@yodaldevoid Genial para escuchar. No sé sobre Gitter, ¡pero definitivamente obtendrás mucha orientación sobre #rustc y / o # rust-internals en IRC!

@yodaldevoid : ¿ hecho hasta ahora aquí .) ¿Quizás podríamos charlar en IRC (#rustc o # rust-internals) al respecto?

@varkor Parece que estás más adelantado que yo. Ciertamente estaría dispuesto a colaborar. Intentaré atraparte en IRC en algún momento y, mientras tanto, veré si he hecho algo que aún no hayas hecho.

¿Hay algún progreso en esto?
Estoy escribiendo código para incrustado, y realmente necesito genéricos const.

@ qwerty19106
Se está avanzando en esto, aunque lentamente. @varkor y yo hemos estado trabajando en esto de vez en cuando a medida que teníamos tiempo. Hice algunos progresos esta semana y estamos viendo la luz al final del túnel para uso básico.

Más allá de simplemente implementar genéricos const, nosotros (varkor) hemos hecho una limpieza para que todo esto sea posible (ver # 48149 y # 48523). Creo que el plan actual es esperar a que se procesen esas dos solicitudes de extracción antes de incorporar genéricos const, pero varkor puede hablar más sobre eso.

Realmente entiendo su necesidad de genéricos const para el trabajo integrado. Comencé con esto porque yo también realmente quiero genéricos const para poder limpiar y hacer grandes franjas de código incrustado seguras.

TL; DR: El progreso va, pero esto es complejo. Te siento en el frente incrustado.

Para la casilla de verificación "documentación", sería genial obtener algo en la guía rustc .

Gracias @yodaldevoid por tu respuesta. Esperaré el final de su trabajo.

Actualización para los curiosos (ya que yo también tenía curiosidad). Re: los dos RP mencionados anteriormente : # 48523 se ha fusionado y # 48149 está progresando constantemente.

@ mark-im ¡Cosas buenas! Buen trabajo de @varkor. ¿Cuándo es ETA aproximadamente, lo sabes? :-)

Saludos, @ flip111.

Parece que el segundo gran PR # 48149 de refactorización se fusionó :)

/ cc yo

Siguiente @varkor PR # 51880

Solo quería dar una actualización rápida, ya que sé que algunas personas han estado preguntando sobre el progreso con los genéricos const.

Para dar un poco de contexto, en marzo , @yodaldevoid y yo tuvimos una implementación inicial que estaba casi funcionando (la mayor parte de la implementación parecía estar terminada, y solo estábamos limpiando algunos bloqueos restantes). Sin embargo, la rama en la que estábamos trabajando era pre-miri. Cuando se fusionó miri (y en varios RP posteriores), el código para lidiar con la evaluación constante cambió significativamente, lo que significa que mucho de lo que habíamos hecho quedó obsoleto.

Además de eso, se decidió que el código de parámetros genéricos debía limpiarse en general antes de agregar los genéricos const, tanto para mejorar la legibilidad como para cometer errores en los que nos olvidamos de tratar con genéricos const en un caso determinado, más difícil de corregir. hacer. Esto se hizo en https://github.com/rust-lang/rust/pull/48523 , https://github.com/rust-lang/rust/pull/48149 y se completará en https: // github. com / rust-lang / rust / pull / 51880. Estos fueron un poco más complicados de lo que esperaba inicialmente, y han tardado un poco más en llevarse a cabo de lo estimado.

Mientras tanto, @yodaldevoid y yo hemos estado trabajando para que nuestra implementación original sea compatible con todos los cambios posteriores en rustc. Ha tomado un tiempo, pero lo estamos logrando (aunque existe el eterno problema de no tener nunca tanto tiempo como esperabas). Espero que pronto tengamos buenas noticias en ese frente. (Mientras tanto, https://github.com/rust-lang-nursery/chalk ha hecho un buen progreso, lo que debería abordar algunas de las dificultades que @eddyb describió originalmente ).


Algunas personas han preguntado cómo pueden ayudar: creo que en esta etapa, será más fácil para nosotros tratar de terminar la implementación inicial y luego ver qué partes necesitan atención. Una vez que esté listo, necesitaremos muchas pruebas, utilizando genéricos const en diferentes lugares (incluidos los inválidos, para mensajes de error), ¡así que definitivamente ese es un lugar al que podríamos hacer con mucha ayuda! ¡Te avisaremos cuando eso suceda!

Lo siento si no es el lugar apropiado para eso, pero tengo una sugerencia con respecto a la igualdad de las expresiones abstractas const. En general, se reduce directamente a una tipificación totalmente dependiente y un territorio indecidible. Sin embargo, en rust todo eventualmente se instancia con valores / tipos concretos, por lo que podemos afirmar algunas igualdades y posponer su verificación a instancias monomórficas.

Lo que quiero decir es que una forma relativamente simple de verificar la igualdad de las expresiones const abstractas es la siguiente:

  • tratar automáticamente con la igualdad sintáctica (es decir, N+1 == N+1 debería funcionar fuera de la caja)
  • permitir al usuario, en el momento de la definición, agregar ecuaciones como N+M == M+N (¿tal vez en la cláusula where ?). Estas ecuaciones pueden ser usadas por la verificación de igualdad (usando alguna forma de cierre de congruencia). Se rechaza una definición que no escriba check usando estas ecuaciones proporcionadas.
  • en un punto de expansión monomórfica, todas las ecuaciones se pueden verificar calculando las constantes exprs, que ya no son abstractas. Aquí hay una opción de diseño: si una ecuación se reduce a false , podría haber un error de compilación ("las ecuaciones son axiomas") o no se puede crear una instancia del rasgo ("las ecuaciones son restricciones")
  • en un punto de expansión parametrizado, estas ecuaciones se transfieren: si tiene una función f parametrizada por N donde N+0==N , esta ecuación no debe perderse en un llamador g ya que deberá comprobarse en cada lugar monomórfico donde se llame g .

Las ventajas de este método es que no hay necesidad de un demostrador de teoremas, un solucionador SMT o un reescritor aritmético en rustc. "Sólo" una verificación de igualdad sintáctica de tipos, modulo alias y módulo un conjunto de ecuaciones proporcionadas por el usuario.
La desventaja es que es más manual para el usuario, ya que las igualdades aparentemente obvias como M+N=N+M deben agregarse explícitamente.

Ejemplo:

/// this works directly because of syntactic checks
fn add_end<T:Copy, const N: usize>(a: &[T;N], b: T) -> [T;N+1] {
  let x : [T;N+1] = [b;N+1];
  x[0..N] = a;
  x
}

/// this should work already
fn append<T:Copy, const M:usize, const N:usize>(a: &[T;M], b: &[T;N])
  -> [T;{M+N}]
{ … }

/// Here the equation M+M==N must be carried over or checked whenever this function is used
fn sum_pairwise_append<const M: usize, const N: usize>(a: &[i32, M],b: &[i32;N])-> [T;N]
  where M+M == N {
  let mut res : [i32; N] = append(a,a);
  for i in 0 .. N { res[i] += b[i] };
  res
} 


fn main() {
  let a: [i32; 2] = [1;2];
  let b: [i32; 4] = [2;4];
  let _ = sum_pairwise_append(a, b);  // need to check 2+2=4 
}

Ecuaciones como axiomas

Esto significa que e1 == e2 siempre debe ser cierto, por lo que no es realmente una restricción where sino más bien una assert_eq!(e1,e2) que puede ser utilizada por el verificador de tipos. Aquí, el implementador promete que esto siempre es cierto y expone a sus usuarios a errores de compilación si proporcionan un parámetro que refuta la ecuación.

Ecuaciones como restricciones

Aquí, una cláusula where e1 == e2 es una restricción que debe cumplirse para el uso exitoso de este rasgo / función parametrizado. Eso significa que e1 == e2 no tiene que ser siempre válido, lo que puede ser interesante para ecuaciones que solo son verdaderas en un dominio, como (x*y) / z == (y*x) / z que no se instanciaría por z==0 .

@ c-cube Ya tenemos un sistema para "cláusulas where implícitas", al menos para funciones, derivadas de "reglas WF (forma correcta)", es decir, si define un fn foo<'a, 'b>(x: &'a &'b i32) {} entonces requiere que las personas que llaman satisfagan 'b: 'a (como resultado de necesitar WF(&'a &'b i32) -> &'b i32: 'a -> 'b: 'a ).

Podemos reutilizar ese sistema (de hecho, esto ya está implementado) para impulsar las restricciones dadas por la firma (de la forma "esta expresión constante se evalúa correctamente") hasta las personas que llaman, mientras que cualquier cosa que se use solo dentro del cuerpo aún necesitaría where cláusulas.

Casi todo lo que está describiendo parece cercano a lo planeado (incluida una forma de "igualdad sintáctica"), pero no queremos verificaciones de "tiempo de monomorfización", en su lugar podemos tener formas (ya sea implícitas o mediante where cláusulas) para "propagar restricciones a instanciadores", y luego producimos errores donde las restricciones son "todavía demasiado genéricas" (tan desconocidas si se cumplen), o de lo contrario si podemos evaluarlas y no se cumplen.

La mayor parte de todo lo que está describiendo parece estar cerca de lo planeado.

@eddyb , ¿conoces alguna publicación de referencia que discuta estos planes?

@ flip111 Probablemente algunos de los RFC. Quizás @withoutboats lo sepa mejor.

# 51880 está hecho: tada :: tada:

@withoutboats @ oli-obk ¿Qué opinas sobre el bloqueo de la unificación adecuada de expresiones como N + 1 (de, por ejemplo, [T; N + 1] ) al obtener alguna forma básica de evaluación simbólica en miri ?

Creo que ya tendríamos un mecanismo para codificarlo, cuando @varkor agrega ConstValue::{Infer,Param} , podemos usarlo para rastrear valores "(superficialmente) válidos pero desconocidos" (simbólicos), y luego también incluir valor (principalmente enteros) operaciones y llamadas además de eso.

Cualquier decisión de flujo de control que no sea assert aún requeriría valores conocidos, pero assert sí mismo tal vez podría cosificarse como AssertThen(condition, assert message, success value) .
(¡Tanto el condition como el success value podrían ser simbólicos!)

Ser capaz de exportar los valores simbólicos a ty::Const significa que podemos implementar parte de la unificación fuera de miri, tratando (¿la mayoría?) De las operaciones como opacas.

Tenemos que tener cuidado de no asumir nada de variables de inferencia, pero por ejemplo, N + 1 creo que podemos apoyar la unificación de dos Add(Param(N), Bits(1)) juntos.

@varkor Un caso de prueba que creo que debería funcionar con una normalización perezosa, sin unificación:

/*const*/ fn append<const N: usize, T>(xs: [T; N - 1], x: T) -> [T; N] {
    let new = MaybeUninit::<[T; N]>::uninitialized();
    unsafe {
        let p = new.as_mut_ptr() as *mut T;
        (p as *mut _).write(xs);
        p.add(N - 1).write(x);
    }
    new.into_inner()
}

Eso solo funcionaría porque:

  • N - 1 aparece en el nivel de tipo exactamente una vez

    • todo puede referirse a esa expresión, opacamente

    • ( posible solución alternativa: type ArrayWithoutLast<T, const N: usize> = [T; N - 1]; )

  • está en una posición de argumento dentro de la firma

    • (no recuerdo si la posición de retorno también funcionaría. @nikomatsakis?)

    • las personas que llaman pueden demostrar que es WF, proporcionando valores concretos para N

    • "burbujeando" el requisito de WF necesita unificación / ArrayWithoutLast

    • otros usos necesitarían "incrustarse en una cláusula where ", por ejemplo, [T; N - 1]: Sized



      • incluso puede abusar de where [T; N - 1]: (¿eso se ignora hoy? ¡ay!)


      • de nuevo eludir la unificación con where ArrayWithoutLast<T, N>: ...



Entonces, en general, probablemente podamos confiar en las reglas de WF para forzar la "validación" de las expresiones constantes en los usuarios, pero aún querríamos la unificación para otras cosas (incluida la no necesidad de hacks como ArrayWithoutLast ).

53645

Pequeña pregunta que se origina en la discusión de Unidades de medida. ¿Qué debemos hacer si el tipo genérico sobre constante no depende directamente de él? Por ejemplo:

struct Foo<T, const N: usize> {
    a: T,
    // Will such array be "the way" to handle this problem?
    // Or do we want some kind of `std:marker::PhantomConstData`? (which can be a simple
    // type alias to the same array)
    // Or maybe make `PhantomData` to accept constants?
    _phantom: [(), N],
}

_fantoma: [(), N],

Supongo que te refieres a [(); N] , pero esto solo funcionará para usize .

Creo que tendría sentido tener un marker::PhantomConst permita cualquier tipo const .

Hmm ... Releyendo el RFC con los últimos comentarios en mente, me pregunto: ¿se permitiría algo como esto?

struct Foo<T, const V: T> {
    _phantom: PhantomConst<T, V>,
}

No veo que esté prohibido en ningún lado, pero habría pensado que merecería al menos un ejemplo.

@Ekleog : hasta donde yo sé, los tipos de parámetros const pueden no depender de los parámetros de tipo inicialmente (aunque definitivamente tendría sentido para una extensión). (La implementación es más complicada si permitimos esto, por lo que tiene sentido comenzar con la versión más simple).

¿Cómo está el progreso en esto? ¿Conocemos una hora aproximada en la que esto debería ocurrir todas las noches?

@Zauberklavier Tan pronto como se esta solicitud de extracción . Para citarlo:

Hay un largo camino por recorrer

@newpavlov Supuse que se permitirían parámetros constantes sin ser utilizados en campos.
La razón por la que se deben usar los parámetros de tipo es debido a la varianza, pero las constantes no tienen este problema.

@eddyb eso es lo que recuerdo de la discusión de RFC también.

@varkor Entonces, esto significa que PhantomConst propuesto por @cuviper no puede existir en el estado actual de este RFC… ¿verdad? aunque los últimos comentarios parecen señalar que no hay necesidad de PhantomConst todos modos.

¿Los parámetros constantes afectan la varianza de los parámetros de tipo que involucran?

Actualmente, creo que los genéricos const no pueden tener parámetros de tipo en su tipo.

No tengo claro qué podrá hacer la primera versión funcional de esto.

Por ejemplo, si el código de este comentario se compilaría:
https://github.com/rust-lang/rfcs/pull/2581#discussion_r230043717

Si ese código se compilara, sería una forma de hacer cumplir las restricciones para las constantes antes de obtener una sintaxis para él.

@ rodrimati1992 Probablemente puedas hacer (): IsTrue<{N < 128}> sin un tipo separado.
¿Supongo que desea reutilizar la restricción y que realmente funcione? (porque copiar la expresión N < 128 no la hará funcionar, inicialmente)
Podrías usar trait Lt128<const N: usize> = IsTrue<{N < 128}>; , supongo.
También existe la opción de simplemente escribir where [(); 128 - N], , creo (pero no estoy seguro de que garanticemos que entrará en pánico).

@ rodrimati1992 Probablemente puedas hacer (): IsTrue<{N < 128}> sin un tipo separado.
¿Supongo que desea reutilizar la restricción y que realmente funcione? (porque copiar la expresión N < 128 no la hará funcionar, inicialmente)
Podrías usar trait Lt128<const N: usize> = IsTrue<{N < 128}>; , supongo.
También existe la opción de simplemente escribir where [(); 128 - N], , creo (pero no estoy seguro de que garanticemos que entrará en pánico).

Entonces, con los alias de rasgos que podría reescribir, ¿es esto ?:

trait AssertLessThan128<const N:usize>=
    Assert<{N<=128}, (
        Str<"uint cannot be constructed with a size larger than 128,the passed size is:",
        Usize<N>>
    ) >;

La idea con el Assertrasgo está usando un error de tipo para imprimir un mensaje de error incrustado en un tipo, que en el caso de AssertLessThan128 es:

(Str<"uint cannot be constructed with a size larger than 128,the passed size is:",Usize<N>>)

que espero sea más útil que where [(); 128 - N], , ya que le dice en el mensaje de error por qué ocurrió el error en tiempo de compilación.

También puede hacer if !(N < 128) { panic!("...") } en una constante, creo.

Pensé que la arquitectura std :: fmt no estaría allí hasta que lleguen las restricciones const trait (o algo similar).

Con esto, puede tener mensajes de error con múltiples valores (incrustados en tipos).

Tal vez sea mejor esperar a que const generics hable sobre esto, ya que será más fácil mostrar ejemplos ejecutables.

@ rodrimati1992 ver https://github.com/rust-lang/rfcs/blob/master/text/2345-const-panic.md , habrá un caso especial para obtener const panic!() antes de que lo hiciera sea ​​posible a través de otras funciones (supongo que probablemente no permitirá el formato personalizado de valores al principio).

@ rodrimati1992 Ahh, ya veo, ¡eso es realmente un truco inteligente!

¿Cuál es el estado de esto?

El siguiente paso sigue siendo https://github.com/rust-lang/rust/pull/53645 AFAIK.

Esto es correcto. Para brindar una actualización rápida para aquellos que no siguen el PR # 53645, los genéricos const se han compilado y funcionan en al menos un caso de uso simple. Lo que queda es terminar el codegen para otros casos de uso, incluidos los genéricos const en arrys, y una limpieza de salida de errores. Después de eso, el RP debería estar listo para fusionarse y la gente puede comenzar a jugar con él.

Podría estar fuera de tema, pero ¿permitirá esto una variante o bifurcación de Chunks y métodos relacionados para que Item sea ​​una matriz de tamaño fijo en tiempo de compilación en lugar de un segmento? Esto permitiría que un bucle for desestructurara / enlazara a un patrón irrefutable que asignaba los elementos del fragmento a variables, como for (first, second) in arr.chunks(2) que actualmente falla, y supongo (sin ninguna justificación es cierto) que permitiría una mayor optimización en ciertos casos de uso.

@jeffvandyke Es posible que esté pensando en ChunksExact específicamente. Tal cambio rompería la API, por lo que no se puede hacer. Podríamos agregar nuevas API que brinden referencias de matriz en el futuro, pero no podemos romper las existentes.

@jeffvandyke Es posible que esté pensando en ChunksExact específicamente. Tal cambio rompería la API, por lo que no se puede hacer. Podríamos agregar nuevas API que brinden referencias de matriz en el futuro, pero no podemos romper las existentes.

Solo estoy esperando que esto se implemente y se estabilice antes de proponer un PR que agregue dicha API además de ChunksExact y sus variantes :)

Las variantes de tiempo de ejecución y tiempo de compilación tienen sus casos de uso, no siempre se conoce el tamaño de su fragmento de antemano. En cuanto a la optimización, si usa ChunksExact con una constante, debería ser más o menos lo mismo según mis pruebas. El compilador puede optimizar todas las comprobaciones de límites.

para ser implementado y estabilizado

Sugeriría no esperar la estabilización, ya que tal API sería un buen uso más para ayudar a ejercitar esta función para la estabilización.

Supongo que esto todavía no funciona para los bloques impl . Lo intenté

#![feature(const_generics)]

struct The<const Val: u64>();

impl<const Val: u64> The<Val> {
    fn the() {
        println!("{}", Val);
    }
}

pero, como de hecho se advirtió, el compilador se bloqueó con

thread 'rustc' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:2419:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
   1: std::sys_common::backtrace::_print
   2: std::panicking::default_hook::{{closure}}
   3: std::panicking::default_hook
   4: rustc::util::common::panic_hook
   5: std::panicking::rust_panic_with_hook
   6: std::panicking::continue_panic_fmt
   7: rust_begin_unwind
   8: core::panicking::panic_fmt
   9: core::slice::slice_index_order_fail
  10: rustc_resolve::Resolver::resolve_ident_in_lexical_scope
  11: rustc_resolve::Resolver::resolve_path
  12: rustc_resolve::Resolver::resolve_path_without_parent_scope
  13: rustc_resolve::Resolver::smart_resolve_path_fragment
  14: rustc_resolve::Resolver::smart_resolve_path
  15: <rustc_resolve::Resolver<'a> as syntax::visit::Visitor<'tcx>>::visit_ty
  16: syntax::visit::walk_generic_args
  17: syntax::visit::walk_ty
  18: rustc_resolve::Resolver::with_generic_param_rib
  19: rustc_resolve::Resolver::resolve_item
  20: rustc_resolve::Resolver::resolve_crate
  21: rustc::util::common::time
  22: rustc_interface::passes::configure_and_expand_inner
  23: rustc_interface::passes::configure_and_expand::{{closure}}
  24: <rustc_data_structures::box_region::PinnedGenerator<I, A, R>>::new
  25: rustc_interface::passes::configure_and_expand
  26: <rustc_interface::queries::Query<T>>::compute
  27: <rustc_interface::queries::Query<T>>::compute
  28: <rustc_interface::queries::Query<T>>::compute
  29: rustc_interface::queries::<impl rustc_interface::interface::Compiler>::prepare_outputs
  30: rustc_interface::interface::run_compiler_in_existing_thread_pool
  31: <std::thread::local::LocalKey<T>>::with
  32: <scoped_tls::ScopedKey<T>>::set
  33: syntax::with_globals

El error está relacionado con const en impl<const Val: u64> ya que eliminar esa parte causa otros errores pero no falla.

pero felicitaciones a Rust por siquiera considerar esta característica. No tenía idea de si funcionaría, pero la sintaxis parecía natural, lo hice y lo rustc dijo que existía :)

No me sorprende que no esté funcionando, ya que esta característica tan esperada todavía se está analizando a través del compilador mientras hablamos (ver, por ejemplo, # 59008 y # 58581 y el trabajo anterior en # 53645 que fue abandonado porque el PR era demasiado grande , pero aún se mantiene abierto como un rastreador para anunciar el progreso).

Sin embargo, no estoy seguro de si se deben esperar accesos de segmento fuera de los límites de los códigos auxiliares de implementación actuales. @varkor @yodaldevoid , ¿puedes echar un vistazo?

Sí, la advertencia es correcta: los genéricos const aún no son funcionales en ninguna forma. Todavía hay algunas solicitudes de extracción más antes de que estén listas para comenzar a jugar.

Lo siento si este no es el lugar adecuado para hacer preguntas, pero no pude encontrar un lugar mejor. Solo 2 preguntas:

  1. ¿Puede una sola función ser condicionalmente constante? Por ejemplo, una función podría tener 2 firmas como:

    const fn foo<A: const T>(x: T)  // `A` const implements `T`
    fn foo<A: T>(x: A)              // `A` implements `T` normally
    

    En este caso, foo es constante iff A: const T ; si A no implementa const T , aún satisface el límite pero foo ya no es constante. También es importante que el autor pueda especificar cualquier límite genérico para ejemplos más complejos (por ejemplo, where A::Output : Bar ). Un gran ejemplo de esto es incluso la aritmética simple:

    // This only accepts types that const implement `T`
    const fn square_const<T: const Mul>(x: T) -> T {
      x*x
    }
    
    // This accepts types that implement `T` any way, but it is not const
    // This function behaves identically to `square_const`
    // But has a different signature and needs a different name
    fn square<T: Mul>(x: T) -> T {
      square_const(x)
    }
    
    let a: u8 = 5;
    let b: FooNumber = FooNumber::new();
    square_const(a); // `u8` const implements Mul
    square(b); // `FooNumber` implements `Mul` normally, so we need a separate function?
    

    Creo firmemente que definitivamente debería haber una manera de hacer esto, y me sorprende que no se mencione en el RFC (¿a menos que me lo haya perdido?).

  2. _ [menos importante]: _ ¿Habrá alguna forma de detectar en el cuerpo de una función constante si estamos ejecutando en tiempo de compilación o en tiempo de ejecución? Creo que alguna macro similar a cfg!() sería una extensión útil, aunque solo sea por la depuración. cfg!() se evalúa actualmente en tiempo de compilación, así que creo (/ supongo) que debería poder saber si la función se está utilizando o no como una función constante o regular, ya que eso también se determina en la compilación -tiempo. Sin embargo, esto es menos importante que mi primera pregunta.

@ Codificador-256

  1. Sí, consulte https://github.com/rust-lang/rfcs/pull/2632.
  2. No estoy seguro de si eso debería ser posible, aunque dado lo anterior, tampoco estoy seguro de que sea necesario.

@ Coder-256 Ambas preguntas no están relacionadas con los genéricos const, sino con las funciones const. Los genéricos de const son genéricos sobre consts (por ejemplo, foo<2>() ) en lugar de que las funciones puedan ejecutarse en tiempo de compilación. Me imagino que es por eso que no encontró las respuestas a sus preguntas en RFC 2000.

@rpjohnst Gracias, pero creo que podría haber sido confuso. Ya he visto rust-lang / rfcs # 2632 y rust-lang / rfcs # 2000, pero estoy bastante seguro de que esto no se menciona en ninguno de los dos. (¿pero podría estar equivocado?) Lo que estoy preguntando es sobre las funciones condicionalmente constantes. Vea los ejemplos que escribí, ya que es difícil de describir.

@yodaldevoid Vaya, tienes razón, ¿dónde debería preguntar esto?

En cuanto a la pregunta macro, estoy de acuerdo en que realmente no sirve de mucho ahora que lo pienso

Lo que estoy preguntando son las funciones condicionalmente constantes. Vea los ejemplos que escribí, ya que es difícil de describir.

Su definición de square_const se puede usar en lugar de square (es decir: se fuerza a una función con la firma equivalente en tiempo de ejecución). Consulte https://github.com/rust-lang/rfcs/pull/2632 para obtener información sobre este comportamiento (también, ese hilo es el lugar adecuado para hacer preguntas sobre cualquier interacción entre const fn y los límites de los rasgos).

@varkor No estoy convencido de que ese sea el caso (ya que los límites de los rasgos cambian), pero preguntaré en rust-lang / rfcs # 2632.

Informe del accidente:

Código:

#![feature(const_generics)]

use std::marker::PhantomData;

struct BoundedU32<const LOWER: u32, const UPPER: u32> {
    value: u32,
    _marker: PhantomData<(LOWER, UPPER)>,
}

Compilador:

thread 'rustc' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:2419:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports

note: rustc 1.35.0-nightly (719b0d984 2019-03-13) running on x86_64-unknown-linux-gnu

note: compiler flags: -C codegen-units=1 -C debuginfo=2 --crate-type lib

note: some of the compiler flags provided by cargo are hidden

error: Could not compile `playground`.

To learn more, run the command again with --verbose.

@Jezza : los genéricos const aún no están completamente implementados y no se espera que funcionen. Haremos un anuncio cuando sea el momento de comenzar a experimentar con #![feature(const_generics)] (que se le notificará si está suscrito a este problema).

@varkor Espera, el ejemplo de @Jezza tiene _marker: PhantomData<(LOWER, UPPER)> - esos son dos const parámetros usados ​​como tipos, ¿por qué rustc_resolve produjo un error?

Buen punto: investigaré esto. Tenga en cuenta que esto es solo un problema con #![feature(const_generics)] , por lo que no es un problema crítico (el uso de consts no genéricas produce el error como se esperaba). Quizás nunca llegue a rustc_resolve .

~ @eddyb : este ICE proviene de resolve_ident_in_lexical_scope , así que imagino que probablemente esté relacionado con https://github.com/rust-lang/rust/issues/58307.~

Editar: en realidad, tal vez no, eso parece aplicarse solo a macro_rules! .

Minimizado:

#![feature(const_generics)]

struct S<const C: u8>(C);

Resuelva los ICE antes de producir el error "tipo esperado, valor encontrado".

El índice está fuera de los límites aquí:
https://github.com/rust-lang/rust/blob/master/src/librustc_resolve/lib.rs#L3919

Current nightly produce "el parámetro N nunca se usa" para el siguiente código:

struct Foo<const N: usize> { }

De la discusión anterior pensé que la compilación debería aceptar este código. ¿Es simplemente un artefacto de la implementación inconclusa?

@newpavlov deben usarse parámetros de tipo normal, ¿debería uno poder hacer PhantomData<n> ?

Supongo que ya es posible hacer PhantomData<[(); N]> . Sin embargo, no estoy seguro de que sea algo que realmente queremos hacer cumplir, ya que AFAIU, el punto de PhantomData es marcar la varianza, y AFAIU no tiene la noción de varianza wrt. un parámetro genérico constante.

Y eso solo funciona cuando N es del tipo usize .

Decidimos que no era necesario usar parámetros const durante la discusión de RFC y la implementación actual es un error.

@withoutboats ¿Puede señalar en qué parte del RFC se indica? Intenté encontrar algo en ese sentido y debí haberlo perdido.

No sé si se incluyó en el texto RFC

@withoutboats ¿Le importaría señalar dónde se discutió eso? Busqué a través del RFC PR, pero no me llamó la atención.

@yodaldevoid Se hizo referencia a este comentario anteriormente: https://github.com/rust-lang/rust/issues/44580#issuecomment -419576947. Pero no fue en el RFC PR, más bien sobre este tema.

No puedo rastrear el historial de comentarios de hace varios años, pero puedo explicar: se requiere el uso de variables de tipo como un bloqueador para que piense en la varianza de esos parámetros (en mi opinión, esto también es innecesario y podríamos usar covariante de forma predeterminada, pero ese es un tema aparte). Los parámetros de constante no tienen ninguna interacción con la varianza, por lo que esto no tendría ninguna motivación.

@ HadrienG2 Gracias por encontrar un comentario relevante.

@withoutboats Realmente no esperaba que

Gracias por la explicación. Debo admitir que nunca puedo pensar en la varianza, no importa cuántas veces trate de aprenderla, pero incluso sin eso, cuando lo pienso de nuevo, no requiere el uso de parámetros de costo tiene sentido. Incluiré la corrección de este error en nuestra lista de FIXMEs.

Aquí hay un resumen del progreso hasta ahora en genéricos const.


Antes de que el trabajo sobre los genéricos const pudiera comenzar correctamente, era necesario realizar una refactorización. @jplatte asumió la tarea con https://github.com/rust-lang/rust/pull/45930. @jplatte luego comenzó a trabajar en la implementación principal de genéricos const, pero descubrió que no tenían tiempo suficiente para continuar.

@yodaldevoid y yo continuamos donde lo dejó https://github.com/rust-lang/rust/pull/48149 , https://github.com/rust-lang/rust/pull / 48452 , https://github.com/rust-lang/rust/pull/48523 , https://github.com/rust-lang/rust/pull/51880.

Una vez hecho esto, la implementación de genéricos const podría comenzar en serio. Desde entonces, @yodaldevoid y yo hemos ido agregando gradualmente soporte para genéricos const: https://github.com/rust-lang/rust/pull/58191 , https://github.com/rust-lang/rust / pull / 58503 , https://github.com/rust-lang/rust/pull/58581 , https://github.com/rust-lang/rust/pull/58583 , https://github.com/rust -lang / rust / pull / 59170 , https://github.com/rust-lang/rust/pull/59355 , https://github.com/rust-lang/rust/pull/59415 , https: // github .com / rust-lang / rust / pull / 60058 , https://github.com/rust-lang/rust/pull/60280 , https://github.com/rust-lang/rust/pull/60284 y la mayoría recientemente https://github.com/rust-lang/rust/pull/59008. (Estos en su mayoría se han separado de la solicitud de extracción de genéricos const principal ).


¿Cuál es el estado ahora? Algunas pruebas genéricas const ahora funcionan 🎉Sin embargo, todavía hay algunas que no lo hacen, y hay una serie de FIXME s en todo el código que aún tenemos que abordar. Sin embargo, ahora nos estamos acercando, y estamos en la etapa en la que hay algo de fruta madura si quieres ayudar.

  • Primero, necesitamos más pruebas. Hasta ahora solo hay un puñado de pruebas genéricas const , pero nos gustaría muchas más. Por el momento, debido a que somos conscientes de una serie de problemas, no necesitamos informes de errores, pero sí necesitamos pasar las pruebas. Si se le ocurre un caso de prueba que funciona (y no se parece demasiado a una prueba existente), no dude en abrir una solicitud de extracción para agregarlo.
  • En segundo lugar, como se mencionó, hay una serie de FIXME(const_generics) repartidos por todo el código. Estamos planeando trabajar en nuestro camino a través de ellos, pero @yodaldevoid y yo solo tenemos mucho tiempo, así que si crees que puedes abordar uno, adelante (aunque es posible que quieras dejar un comentario que diga lo mismo, así que no lo hacemos. t duplicar esfuerzos).

Escribí una descripción general de algunos de los problemas de implementación restantes antes de que los genéricos const estén listos para las pruebas adecuadas, en la publicación superior, para dar una idea aproximada de lo que queda por hacer.

Ha llevado tiempo, pero hemos estado progresando constantemente y esperamos mantener el ritmo. ¡Es motivador ver finalmente que las cosas comienzan a encajar!

He estado esperando meses para esta publicación @varkor :) No soy un mago de Rust, pero me gustaría abordar uno de los FIXME s

Felicitaciones @jplatte , @yodaldevoid y @varkor. Este es un gran paso para deshacerse de los rasgos personalizados Array-like en las bibliotecas matemáticas.

Publicación cruzada ...

@varkor En cuanto a las pruebas, quizás sería útil saber qué se espera que funcione. Por ejemplo, me sorprendió que esto no funcione: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=d84ffd15226fcffe02c102edb8ae5cf1

Además, para referencia de aquellos interesados ​​en FIXMEs: https://oli-obk.github.io/fixmeh/

@ mark-im intente poner {} alrededor de FOO . De esa forma funcionará.

Citando https://github.com/rust-lang/rfcs/blob/master/text/2000-const-generics.md :

Cuando se aplica una expresión como parámetro const (excepto para matrices), que no es una expresión de identidad, la expresión debe estar contenida dentro de un bloque. Esta restricción sintáctica es necesaria para evitar que se requiera una búsqueda anticipada infinita al analizar una expresión dentro de un tipo.

{expression} solo debería ser necesario si la expresión no es un identificador o un literal, eso es un error.

Citando el mismo RFC:

Expresión de identidad: una expresión que no se puede evaluar más a menos que se sustituya por nombres en el ámbito. Esto incluye todos los literales, así como todos los identificadores, por ejemplo, 3, "Hola, mundo", foo_bar.

@ mark-im Sí, actualmente no podemos diferenciar entre idents para tipos y consts cuando analizamos inicialmente. Dejamos de tomar la decisión de cómo manejar todo eso en el futuro. Supongo que ahora podría ser el futuro.

Para un poco de historia, hemos hablado de dos formas en las que puedo recordar cómo manejar esto. La primera forma es obligar a las personas a marcar argumentos constantes (ya sea escribiendo const antes de ellos o de alguna otra manera). Esto no es genial desde el punto de vista de la ergonomía, pero es fácil desde el punto de vista del análisis. La segunda forma es tratar todos los argumentos genéricos como iguales hasta que comencemos a emparejarlos con parámetros genéricos más adelante durante la compilación. Este es probablemente el método que queremos tomar, y ya se ha trabajado un poco para hacerlo posible, pero no hemos dado el salto final.

Increíble trabajo. Estoy feliz de ayudar, arreglando algunos de los FIXME , si puedo. No tengo mucha experiencia en la base de código rustc, así que comenzaré con FIXME https://github.com/rust-lang/rust/blob/master/src/librustc_mir/monomorphize/item.rs# L397 , ya que parece que sería fácil.

¿Qué pasa con el siguiente código:

trait Foo {
    const N: usize;
    fn foo() -> [u8; Self::N];
}

Actualmente da como resultado "ningún elemento asociado llamado N encontrado para el tipo Self en el alcance actual" error de compilación. ¿Se aceptará dicho código después de terminar FIXME s o requerirá un esfuerzo adicional para implementarlo?

@yodaldevoid

Pregunta rápida, disculpas si esto ya se ha discutido.

La segunda forma es tratar todos los argumentos genéricos como iguales hasta que comencemos a emparejarlos con parámetros genéricos más adelante durante la compilación. Este es probablemente el método que queremos tomar, y ya se ha trabajado un poco para hacerlo posible, pero no hemos dado el salto final.

¿No va esto más bien contra la corriente en el principio que Rust asume al hacer explícitas las firmas de funciones para evitar producir errores relacionados con la implementación de una función? Sin embargo, tal vez no entiendo esto por completo y estás hablando de analizar parámetros genéricos dentro del cuerpo de una función.

¿No va esto más bien contra la corriente en el principio que Rust asume al hacer explícitas las firmas de funciones para evitar producir errores relacionados con la implementación de una función? Sin embargo, quizás estoy entendiendo esto totalmente mal y estás hablando de analizar parámetros genéricos dentro del cuerpo de una función.

Se trata de determinar si los identificadores pasados ​​como parámetros genéricos a una función son constantes o tipos.

Ejemplo:

fn greet<const NAME:&'static str>(){
    println!("Hello, {}.",NAME);
}
const HIS_NAME:&'static str="John";
greet::<HIS_NAME>();
greet::<"Dave">();

Tenga en cuenta que al definir la función, debe especificar que NAME es una constante, pero cuando la llame, no es necesario decir que HIS_NAME es una constante dentro del operador turbofish ( ::< > ).

@zesterer Si esta solución propuesta se hiciera transparente para el usuario (por ejemplo, al definir una función no había diferencia entre los tipos de parámetros), estaría en lo correcto. Sin embargo, la idea detrás de la solución propuesta no es cambiar el comportamiento de cara al usuario, sino solo cambiar la implementación. El usuario aún escribiría firmas de funciones con parámetros explícitos de tipo, const y de duración, y los argumentos genéricos aún necesitarían coincidir con un parámetro correspondiente, simplemente cambiaría la forma en que lo analizamos en el compilador.

@newpavlov Me sorprende que el código no funcione. Si pudiera enviar un número por separado, se lo agradecería.

@yodaldevoid @robarnold

Ah, te tengo. Supuse erróneamente que esto estaba relacionado con firmas de tipo / función.

Para usar genéricos const en tipos de matriz incorporados, abrí # 60466 para ver las opiniones de otros sobre esto.

Los bloques de Impl parecen completamente rotos en este momento. ¿Se espera esto en el estado actual de la función o debo abrir otro problema al respecto?

Los bloques de Impl parecen completamente rotos en este momento. ¿Se espera esto en el estado actual de la función o debo abrir otro problema al respecto?

Debe agregar {} s alrededor de N , impl<const N: usize> Dummy<{N}> {} .

Pero sí, entonces obtendrás un error sobre el parámetro sin restricciones.

Ah gracias. ¡Olvidé lo de los aparatos ortopédicos!

No me sorprende que falle una vez que se resuelva, ya que este fue un caso de prueba muy minimizado.

... pero sí, este probablemente debería funcionar:

#![feature(const_generics)]

trait Dummy {}

struct Vector<const N: usize> {
    data: [f32; N],
}

impl<const N: usize> Dummy for Vector<{N}> {}

... y todavía falla con E0207:

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates
 --> src/lib.rs:9:12
  |
9 | impl<const N: usize> Dummy for Vector<{N}> {}
  |            ^ unconstrained const parameter

error: aborting due to previous error

For more information about this error, try `rustc --explain E0207`.
error: Could not compile `small-matrix`.

To learn more, run the command again with --verbose.

... y supongo que esto es lo que @varkor quiso decir con "problemas con el manejo de matrices para consts genéricos" en # 60466:

#![feature(const_generics)]

fn dummy<const N: usize>() -> [f32; N] {
    [0.; N]
}

->

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: cat_expr Errd
 --> src/lib.rs:3:44
  |
3 |   pub fn dummy<const N: usize>() -> [f32; N] {
  |  ____________________________________________^
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: cat_expr Errd
 --> src/lib.rs:4:5
  |
4 |     [0.; N]
  |     ^^^^^^^

error: internal compiler error: QualifyAndPromoteConstants: Mir had errors
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: broken MIR in DefId(0/0:3 ~ small_matrix[5b35]::dummy[0]) ("return type"): bad type [type error]
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: broken MIR in DefId(0/0:3 ~ small_matrix[5b35]::dummy[0]) (LocalDecl { mutability: Mut, is_user_variable: None, internal: false, is_block_tail: None, ty: [type error], user_ty: UserTypeProjections { contents: [] }, name: None, source_info: SourceInfo { span: src/lib.rs:3:1: 5:2, scope: scope[0] }, visibility_scope: scope[0] }): bad type [type error]
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

thread 'rustc' panicked at 'no errors encountered even though `delay_span_bug` issued', src/librustc_errors/lib.rs:356:17
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports

note: rustc 1.36.0-nightly (cfdc84a00 2019-05-07) running on x86_64-unknown-linux-gnu

note: compiler flags: -C debuginfo=2 -C incremental --crate-type lib

note: some of the compiler flags provided by cargo are hidden

@ HadrienG2
Vea # 60619 y # 60632.

Creo que este código debería compilarse:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=e45b7b5e881732ad80b7015fc2d3795c

pero actualmente errores:

error [E0119]: implementaciones conflictivas del rasgo std::convert::TryFrom<[type error]> para el tipo MyArray<_, _> :

Desafortunadamente, esa es una restricción de Try{From,Into} y no tiene nada que ver con los genéricos const. No se pueden implementar de forma genérica en muchos casos: parque infantil

@oberien en su ejemplo, T es un tipo extranjero, y no se sabe si T: Into<Foo<T>> cumple o no.

En mi ejemplo, [T; N] es un tipo definido en libcore, y es #[fundamental] (tbh, ni siquiera sé lo que esto significa), así que creo que el solucionador de rasgos realmente sabe que [T; N]: Into<MyArray<T, {N}>> no se mantiene, y ahí no debería ser un conflicto, creo?

[...] y es #[fundamental] [...]

Este es el problema, fundamental está relacionado con permitir que los usuarios posteriores definan implementaciones de rasgos cuando envuelven un tipo local en el tipo de envoltura fundamental. Puede ver en este campo de juego que su implementación funciona entre tipos no fundamentales, pero falla si el tipo from está marcado como fundamental (con un mensaje de error mucho mejor).

@varkor @yodaldevoid Entonces, acabo de terminar en una situación en la que S<{N == 0}> (con S tomando un parámetro const bool y N a const usize ) no es ni S<{true}> o S<{false}> en el ojo del compilador (en el sentido de que no implementa los mismos rasgos que ninguno de los dos), y no estoy seguro si esto es un error o un limitación esperada del prototipo actual. ¿Puede darme un repaso rápido sobre la lógica de unificación actual?

Como ejemplo minimizado, esto ...

// Machinery for type level if-then-else
struct Test<const B: bool>;
trait IfFn<S, Z> { type Out; }
impl<S, Z> IfFn<S, Z> for Test<{true }> { type Out = S; }
impl<S, Z> IfFn<S, Z> for Test<{false}> { type Out = Z; }

// Returns an u8 if B is true, else an u16
fn should_be_ok<const B: bool>() -> <Test<{B}> as IfFn<u8, u16>>::Out {
    0
}

... produce el siguiente error:

error[E0277]: the trait bound `Test<B>: IfFn<u8, u16>` is not satisfied
  --> src/lib.rs:32:1
   |
32 | / fn should_be_ok<const B: bool>() -> <Test<{B}> as IfFn<u8, u16>>::Out {
33 | |     0
34 | | }
   | |_^ the trait `IfFn<u8, u16>` is not implemented for `Test<B>`
   |
   = help: the following implementations were found:
             <Test<false> as IfFn<S, Z>>
             <Test<true> as IfFn<S, Z>>

En realidad no lo sé, pero creo que el problema puede ser que el compilador no tiene la lógica para decidir si el 0 literal es un u8 o un u16 . Prueba algo como: 0u8 as _

Eso no es todo, el mensaje de error sigue siendo el mismo.

Pero si desea una versión que no implique la inferencia de tipo entero, aquí hay una minimización más tonta:

struct Test<const B: bool>;
trait Not { const B: bool; }
impl Not for Test<{true }> { const B: bool = false; }
impl Not for Test<{false}> { const B: bool = true; }

fn should_be_ok<const B: bool>() -> bool {
    <Test<{B}> as Not>::B
}

Aún falla debido a que Test<{B}> supuestamente no implementa Not .

Hola ! No estoy realmente seguro de si se supone que la unificación en valores constantes funciona o no, pero hay un nuevo sistema de unificación en proceso ("tiza") en el que se está trabajando, pero no es el que rustc usa actualmente.
Entonces, lo que está tratando de hacer _ puede_ tener que esperar hasta que la tiza esté lista. E incluso entonces, no estoy seguro de que funcione o se suponga que funcione incluso con tiza.
Consulte https://github.com/rust-lang/rust/issues/48049 para conocer el progreso.

@ HadrienG2, ¿ podría presentar un problema por separado para eso? No me sorprende del todo que no funcione en este momento debido a algunos otros problemas que estamos investigando, pero este es un caso nuevo y me gustaría que lo rastrearan.

La unificación de

@ HadrienG2 una vez que tengamos especialización en genéricos const, podrá hacer algo como

struct Test<const B: bool>;
trait Not { const B: bool; }
impl<const B: bool> Not for Test<B> { const B: bool = false; }
impl Not for Test<{false}> { const B: bool = true; }

fn should_be_ok<const B: bool>() -> bool {
    <Test<{B}> as Not>::B
}

Lo que quiere hacer es algún tipo de control exhaustivo como el que existe para la coincidencia que se acaba de extender al sistema de rasgos. No sé si se menciona en ningún hilo.

@yodaldevoid Archivado como https://github.com/rust-lang/rust/issues/61537 .

@carado Chalk no se refiere a las "primitivas del sistema de tipos", de las cuales esta es una.
Es decir, rustc todavía tiene que (incluso hoy, dado que Chalk ya está parcialmente integrado) implementar la parte de la unificación que trata con entidades opacas a Chalk (como dos expresiones constantes diferentes).
Si implementamos eso (para lo cual no tenemos ningún plan de futuro cercano, de todos modos), no cambiará mucho incluso después de que Chalk reemplace el sistema de rasgos (que es su propósito principal, no la unificación).

¡Oh, mi mal entonces! Supongo que realmente no sé de qué estoy hablando.

Una de las cosas asombrosas que proporcionarían los genéricos const es funciones simples calculadas en tiempo de compilación, por ejemplo factorial.

Ejemplo:

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<i - 1>().unwrap() + i)
    }
}

Por lo que tengo entendido, ¿hay algún soporte limitado para los genéricos const en la noche? Si es así, ¿hay algún lugar más organizado que este número donde pueda encontrar cómo usarlo y esas cosas?

@ dancojocaru2000 const funciones deben ser la forma preferida para el cálculo de nivel de valor en tiempo de compilación, por ejemplo, https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=4994b7ca9cda0bfc44f5359443431378 , lo que sucede que no funcionen porque aún no se han implementado por completo. Pero tampoco lo son const genéricos.

Su fragmento puede ser problemático porque no estoy seguro de si se supone que match funciona para const argumentos. También mezcló la suma con la multiplicación en el código, pero eso no importa demasiado.

Creo que ya puede hacer factoriales en tiempo de compilación con una codificación Peano, que a menudo se muestra como una demostración para otros lenguajes funcionales.

compilar funciones simples calculadas en tiempo

Creo que la característica más apropiada sería const fn .

Teóricamente, su código funcionaría casi como está

#![feature(const_generics)]

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<{X - 1}>().unwrap() + i)
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

Pero no lo hace:

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/main.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: src/librustc_codegen_ssa/mir/operand.rs:79: unevaluated constant in `OperandRef::from_const`

Aunque realmente deberías usar un tipo sin firmar:

#![feature(const_generics)]

fn factorial<const X: u32>() -> u32 {
    match X {
        0 => 1,
        1 => 1,
        _ => factorial::<{ X - 1 }>() + X,
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

¿Hay algún lugar más organizado que este número donde pueda encontrar cómo usarlo y esas cosas?

Eso suele estar cubierto por el libro inestable , pero ahora no hay nada útil allí. A medida que descubra los bits, ¿quizás podría considerar comenzar a esbozar algún contenido para eso?

factoriales en tiempo de compilación con una codificación Peano,

Para ver un ejemplo de eso, vea la caja typenum

Teóricamente, su código funcionaría casi como está

Al llamar a factorial::<0> el _ => factorial::<{X - 1}>() * X también se codificaría, ¿verdad? Pero intentar hacerlo causaría un subdesbordamiento de enteros.

¿Puedo esperar que un código como este se compile en el futuro?

#![feature(const_generics)]

trait NeedsDrop<const B: bool> { }
impl<T> NeedsDrop<std::mem::needs_drop<T>()> for T { }

fn foo<D: NeedsDrop<false>>(d: D) { }

Esto se cruza con una implementación reciente de algunas matrices [T; N] para N <= 32 como genéricos const, pero el siguiente código no se compila en una última ( 4bb6b4a5e 2019-07-11 ) todas las noches con varios errores the trait `std::array::LengthAtMost32` is not implemented for `[u64; _]`

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct BigintRepresentation<
    const N: usize
>(pub [u64; N]) 
where [u64; N]: std::array::LengthAtMost32, 
   [u64; N*2]: std::array::LengthAtMost32;

aunque lo siguiente lo hace:

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct BigintRepresentation<
    const N: usize
>(pub [u64; N]) 
where [u64; N]: std::array::LengthAtMost32;

Parece que N*2 no es una constante o calificador válido para dicha restricción

@shamatar N*2 debe estar rodeado de llaves como `[u64; {N * 2}]

Creo que los errores de "rasgo no implementado" provienen de las macros de derivación que no agregan el límite para [u64; {N*2}] , pero si se eliminan las derivaciones, hay un ICE actualmente:

`` `error: error interno del compilador: la constante en el tipo tenía un error ignorado: TooGeneric
-> src / main.rs: 5: 1
|
5 | / pub struct BigintRepresentation <
6 | | const N: usize
7 | | > (pub [u64; N])
8 | | donde [u64; N]: estándar :: matriz :: LengthAtMost32,
9 | | [u64; {N * 2}]: std :: array :: LengthAtMost32;
| | ____________________________________________ ^

el hilo 'rustc' entró en pánico ante 'no se encontraron errores aunque delay_span_bug emitidos', src / librustc_errors / lib.rs: 366: 17
nota: ejecute con la variable de entorno RUST_BACKTRACE=1 para mostrar un seguimiento.

Este código no compila:

#![feature(const_generics)]

struct Foo<const X: usize>([u8; X]);

impl<const X: usize> Foo<X> {
    fn new() -> Self {
        Self([0u8; X])
    }
}
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error[E0573]: expected type, found const parameter `X`
 --> src/lib.rs:5:26
  |
5 | impl<const X: usize> Foo<X> {
  |                          ^
  |                          |
  |                          not a type
  |                          help: try using the variant's enum: `regex_syntax::ast::HexLiteralKind`

error[E0107]: wrong number of const arguments: expected 1, found 0
 --> src/lib.rs:5:22
  |
5 | impl<const X: usize> Foo<X> {
  |                      ^^^^^^ expected 1 const argument

error[E0107]: wrong number of type arguments: expected 0, found 1
 --> src/lib.rs:5:26
  |
5 | impl<const X: usize> Foo<X> {
  |                          ^ unexpected type argument

error: aborting due to 3 previous errors

@npmccallum necesitas hacer Foo<{X}>

@ pengowen123 Sí, compila (bloquea el compilador con constant in type had an ignored error: TooGeneric ) sin derive , pero entonces puede ser una pregunta separada si tal "doble restricción" que es efectivamente N <= 32 compilador no aplica N <= 16 .

Las expresiones que mencionan parámetros probablemente no funcionarán por un tiempo más, [T; N] y Foo<{N}> están en mayúsculas especiales para no tener expresiones con una mención de N en ellas, sino consulte directamente el parámetro N , omitiendo el problema mayor.

El uso de Self provoca un bloqueo.
Incluso cambiándolo por Value<{C}> , todavía falla.

#![feature(const_generics)]

struct Value<const C: usize>;

impl<const C: usize> Value<{C}> {
    pub fn new() -> Self {
        unimplemented!()
    }
}

pub fn main() {
    let value = Value::new();
}

Igual que # 61338, el origen del problema es la compilación incremental.

compilar funciones simples calculadas en tiempo

Creo que la característica más apropiada sería const fn .

Teóricamente, su código funcionaría casi como está

#![feature(const_generics)]

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<{X - 1}>().unwrap() + i)
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

Pero no lo hace:

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/main.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: src/librustc_codegen_ssa/mir/operand.rs:79: unevaluated constant in `OperandRef::from_const`

Aunque realmente deberías usar un tipo sin firmar:

#![feature(const_generics)]

fn factorial<const X: u32>() -> u32 {
    match X {
        0 => 1,
        1 => 1,
        _ => factorial::<{ X - 1 }>() + X,
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

Esta función también es útil para mí, const fn no es suficiente. Quiero usarlo para una matriz N-dimensional con una condición de terminación de dimensiones = 0.

Puedo crear una estructura:

#![feature(const_generics)]
struct MyArray<T: Default, const len: usize> {
    real_array: [T; len]
}

impl<T: Default, const len: usize> MyArray<T, {len}> {
    fn new() -> Self {
        return MyArray {
            real_array: [Default::default(); len]
        }
    }
}

Esto falla porque en realidad no puedo inicializar la matriz debido a:

error: array lengths can't depend on generic parameters
 --> src/main.rs:9:46
  |
9 |             real_array: [Default::default(); len]
  |                                              

He intentado solucionarlo haciendo que los valores constantes se establezcan en el valor genérico.

Eso supera el error anterior, pero el compilador falla.

error: internal compiler error: src/librustc/ty/subst.rs:597: const parameter `height/#0` (Const { ty: usize, val: Param(height/#0) }/0) out of range when substituting substs=[]

El lenguaje necesita desesperadamente genéricos básicos constantes, de modo que la biblioteca estándar no tenga que definir cada función para cada tamaño de matriz. Esta es la única razón por la que no uso óxido. ¿Realmente necesitamos realizar funciones completas de tiempo de compilación?
Puede que me equivoque, pero las expresiones simples de enteros deberían ser suficientes y espero que nadie esté perdiendo el tiempo, asegurándose de que estos ejemplos extravagantes funcionen.

El lenguaje necesita desesperadamente genéricos básicos constantes, de modo que la biblioteca estándar no tenga que definir cada función para cada tamaño de matriz.

Ya hay algún esfuerzo en https://github.com/rust-lang/rust/issues/61415.

Espero que nadie pierda el tiempo asegurándose de que estos ejemplos disparatados funcionen.

Algunas personas lo hacen, no puedo ver ningún problema con eso;)

Esta es la única razón por la que no uso óxido.

Esta es la razón menos interesante que he visto citada para alguien que no usa óxido. ¿Estás seguro de que estás diciendo la verdad?

pero las expresiones enteras simples deberían ser suficientes

Hacer incluso ese alcance reducido es increíblemente difícil. Pruébelo, tenemos gente realmente capaz haciendo esto y hay una razón por la que está tomando tanto tiempo.

Desafortunadamente, no he podido dedicar mucho tiempo a corregir errores genéricos const recientemente (o a Rust en general). Si alguien quiere involucrarse en impulsar los genéricos const, me complacerá ofrecer consejos para abordar los problemas abiertos y revisar las correcciones de errores, aunque probablemente pasará un tiempo antes de que pueda concentrarme en esto nuevamente.

Espero que nadie pierda el tiempo asegurándose de que estos ejemplos disparatados funcionen.

Nadie lo es, miri ya es mucho más poderoso que C ++ constexpr .
Y nadie está trabajando en algo sofisticado como derivar N = M de N + 1 = M + 1 .

La mayoría de estos errores no tienen que ver con las expresiones que contienen, sino con el sistema de tipos y cómo interactúan los genéricos const con todas las demás funciones.
Todavía estarían allí si todo lo que tuviera fueran const genéricos y literales enteros.

Por cierto, creo que el error "las longitudes de la matriz no pueden depender de los parámetros genéricos" para [expr; N] no son necesarias, podríamos usar el mismo truco que hacemos para los tipos [T; N] y extraer sacar el N sin evaluarlo como una expresión.

Si bien quiero intentarlo, no estoy seguro de ser la persona adecuada. He usado poco óxido y conozco muy poca teoría de compiladores. Puede que necesite un poco de entrenamiento, pero ciertamente estoy dispuesto. 😄

Editar: Sin embargo, tengo un poco de experiencia en software en general.

@varkor , he estado buscando algo útil que hacer en rustc , y me encantaría dar un paso al frente y ayudar. Además, ¡gracias por ser sincero sobre su capacidad para dedicarle tiempo!

@varkor , en realidad estoy siguiendo el comentario anterior, así que si tienes algún consejo, ¡soy todo oídos! No dude en referirme a otro canal de comunicación también.

Una cosa que podríamos hacer ahora mismo, me acabo de dar cuenta es permitir que las expresiones Foo::<{...}> / [expr; ...] refieran a parámetros genéricos (en la parte ... ).

Esto se debe a que las expresiones deben estar anidadas en algún lugar dentro de un cuerpo y eso tiende a evitar dependencias cíclicas.

Sin embargo, me preocupa que, por ejemplo, tener [(); [0; 1][0]] en una firma se rompa, por lo que probablemente necesitemos un cráter de todos modos (y hoy en día eso lleva una eternidad).

Para cualquiera que esté interesado en ayudar con los genéricos const, mi consejo sería echar un vistazo a la lista de problemas de genéricos const abiertos e investigar el que le parezca interesante. Algunos ya han tenido alguna investigación, que debería estar en los comentarios. La mayoría de los problemas probablemente requieran un poco de investigación. Es útil hacer un comentario si planea investigar algo, por lo que no duplicamos esfuerzos (pero a menudo puede ignorar al responsable del problema si no ha habido ninguna actividad durante un tiempo). Si tiene alguna pregunta, puede preguntar en los comentarios del problema o en Discord o Zulip. @eddyb , @yodaldevoid , @ oli-obk y yo estamos familiarizados con muchas de las áreas relevantes y somos buenas personas para preguntar. ¡Gracias a todos por su interés!

cc @hameerabbasi , @ranweiler

Preguntas sobre genéricos const:

  1. ¿Dónde puedo encontrar documentación (para no tener que hacer preguntas aquí)?
  2. ¿Qué limitaciones existen en este momento sobre el uso de un parámetro const de tipo arbitrario?
  3. ¿Qué características importantes de const genéricos no se implementaron / bloquearon el compilador?

PD (Colaboradores :) Muchas gracias por trabajar en esta función.

const-generics aún está en desarrollo, por lo que aún no hay ningún documento oficial al respecto. No estoy muy seguro de las otras dos preguntas. La última vez que utilicé const-generics, se bloquearon cuando especifiqué algunos parámetros const-generic, pero ha pasado casi un mes desde que hice algo con él por última vez, por lo que las cosas pueden haber cambiado desde entonces.

Y nadie está trabajando en algo sofisticado como derivar N = M de N + 1 = M + 1 .

Sería muy útil tener un solucionador para tales restricciones de tipo. Por ejemplo, al implementar una adición si hay dos N-bits, el tamaño de retorno debe ser (N + 1) para tener en cuenta un bit de desbordamiento. Cuando (por ejemplo) se dan dos números de 5 bits, el solucionador debe verificar que N + 1 = 6 . Con suerte, esto se puede atornillar a los genéricos const más adelante :)

@ flip111 Sí, creo que el plan es agregar esto más adelante, pero este tipo de restricciones de expresión general son muy complejas y difíciles de implementar. Por lo tanto, es posible que no los veamos durante al menos algunos años.

Para ser honesto, resolver ese tipo de problemas basados ​​en restricciones suena muy parecido a un trabajo para Prolog. ¿Quizás llevar a cuestas el motor de tiza es una opción? Afaik, sin embargo, resuelve los rasgos, ¿verdad?

Por cierto, me encanta el tema, aunque no puedo permitirme ayudar con este cajero automático. Gracias a todos los que están trabajando en Const Generics por su tiempo y compromiso. 💐

Una pequeña actualización: estoy subiendo por el árbol de Rust. Planeo contribuir, pero quiero estudiar por mi cuenta en el lugar donde no necesito un entrenamiento excesivo.

Puedo crear una estructura:

#![feature(const_generics)]
struct MyArray<T: Default, const len: usize> {
    real_array: [T; len]
}

impl<T: Default, const len: usize> MyArray<T, {len}> {
    fn new() -> Self {
        return MyArray {
            real_array: [Default::default(); len]
        }
    }
}

Esto falla porque en realidad no puedo inicializar la matriz debido a:

error: array lengths can't depend on generic parameters
 --> src/main.rs:9:46
  |
9 |             real_array: [Default::default(); len]
  |                                              

Me encontré con el mismo problema, pero encontré una solución usando MaybeUninit :
https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=3100d5f7a4efd844954a6fa5e8b8c526
Obviamente, es solo una solución para obtener matrices correctamente inicializadas, pero para mí esto es suficiente hasta que esté disponible una forma adecuada.
Nota: Creo que el código siempre debería funcionar según lo previsto, pero si alguien encuentra un error con el uso de lo inseguro, estaré encantado de solucionarlo.

@raidwas está = para inicializar la memoria no inicializada. Haz esto en su lugar

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=5d962ce7c553e850030240244608ec00

@KrishnaSannasi gracias, buena captura: D (técnicamente no eliminé la memoria no inicializada ya que solo la uso para primitivas, pero es bueno tener una versión correcta aquí)

Técnicamente, dejar caer incluso flotadores no está definido, pero no se puede explotar en este momento.

Técnicamente, dejar caer incluso flotadores no está definido, pero no se puede explotar en este momento.

Habría esperado que eliminar cualquier tipo Copy , incluso los no inicializados, no es una operación. Quizás valga la pena cambiarlo.

Técnicamente, dejar caer incluso flotadores no está definido, pero no se puede explotar en este momento.

Habría esperado que eliminar cualquier tipo Copy , incluso los no inicializados, no es una operación. Quizás valga la pena cambiarlo.

Técnicamente sigue siendo UB. Si bien eliminar de forma segura los valores definidos de Copy no es una operación, el compilador puede decidir hacer algunas optimizaciones inesperadas si intenta eliminar la memoria no inicializada de cualquier tipo (por ejemplo, eliminar todo el código que posiblemente toque ese valor), lo que posiblemente podría romper cosas. Ese es mi entendimiento de eso.

No quiero ser descortés, pero ≥ 60 personas reciben notificaciones sobre los comentarios en este hilo, por lo que probablemente deberíamos mantener la discusión fuera del tema al mínimo. Mejor usemos esto para coordinar el trabajo sobre genéricos const, ya que eso es lo que todos queremos que suceda.

Fallo si usé _alias que apuntan a escribir con const-param_ de otra caja (dependencia).

Por ejemplo:

  • caja A, lib
    `` `#! [crate_type =" lib "]

    ! [función (const_generics)]

tipo de pub Alias= Estructura;
pub struct Struct(T);

- crate B

cajón externo crate_a;
use crate_a :: Alias;

pub fn inner_fn (v: Alias) {}
''
registro de fallos (44580) .txt

@ fzzr- suena como # 64730

He estado cambiando algo de código a genéricos const y parece que realmente hay dos casos de uso diferentes. No estoy seguro de si deberían combinarse o si estaríamos mejor servidos con dos sintaxis diferentes para los casos de uso.

Muchos de mis usos no son en realidad para tipos donde un valor constante juega un papel en la determinación de los tipos, sino más bien para aprovechar las características que están bloqueadas para valores no constantes / literales (aún no son totalmente compatibles, por ejemplo, patrones de coincidencia, pero en última instancia tendrá que serlo).

En mi humilde opinión, deberíamos aterrizar formalmente "argumentos const" junto con genéricos const, para que la gente no escriba código mutante introduciendo mil "genéricos const" (uno para cada argumento) para que el compilador evalúe ciertas variables como literales / constantes.

@mqudsi ¿Podría dar un ejemplo? Ya hay planes y trabajo fundamental en curso para hacer que const eval sea más potente. Sin embargo, eso es ortogonal a los genéricos constantes.

Lo que quiero decir es que si desea refactorizar el código del analizador típico como el siguiente para su reutilización:

let mut result: u32 = 0;
let mut digits = 0;
while digits < max_digits && src.has_remaining() {
    match src.get_u8() {
        d<strong i="6">@b</strong>'0'..=b'9' => {
            result = result*10 + (d - b'0') as u32;
            digits += 1;
        },
        b'>' => match result {
            0..=191 => break, // valid range
            _ => return Err(DecoderError::OutOfRange),
        },
        _ => return Err(DecoderError::Malformed)
    }
}

Por lo general, lo movería a una función, excepto que lo siguiente no funcionará porque ciertos valores que se usan en la coincidencia de patrones se han convertido en variables:

#[inline(always)]
fn read_str_digits<B: bytes::buf::Buf>(src: &mut B, stop_word: u8, 
    max_digits: u8, min_value: u32, max_value: u32) -> Result<u32, DecoderError> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="10">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            stop_word => match result {
                min_value..=max_value => break, // valid range
                _ => return Err(DecoderError::OutOfRange),
            },
            _ => return Err(DecoderError::Malformed)
        }
    }

    ...
}

Si const generics aterriza antes que los argumentos const, de repente puedo abusar de const generics para llegar a lo siguiente:

#[inline(always)]
fn read_str_digits<const MinValue: u32, const MaxValue: u32, const StopWord: u8, B: bytes::buf::Buf>
(src: &mut B, max_digits: u8) -> Result<u32, DecoderError> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="14">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            StopWord => match result {
                MinValue..=MaxValue => break, // valid range
                _ => return Err(DecoderError::OutOfRange),
            },
            _ => return Err(DecoderError::Malformed)
        }
    }

    ...
}

A partir de hoy, incluso este código no funcionará porque el compilador no detecta que los valores genéricos const sean constantes válidas para su uso en un patrón, pero eso es obviamente incorrecto y debe abordarse antes de que RFC 2000 pueda aterrizar. No me malinterpretes, he estado luchando por constantes genéricas durante años y tengo relaciones públicas listas para una docena de cajas importantes (no puedo esperar para convertir la zona horaria en chrono a const genérica y unificar todos los diversos tipos de DateTime ), pero en mi humilde opinión, si es posible abusar de los genéricos const para falsificar argumentos const (donde por "const" lo que realmente queremos decir es "literal"), entonces verá que se ha generalizado abuso de eso.

No es necesariamente el fin del mundo, pero sin haber profundizado demasiado en él, parece que una implementación de genéricos const adecuada y completa necesariamente incluirá todos los argumentos para const de todos modos, por lo que también podríamos tomarnos un tiempo extra. para finalizar la sintaxis / ux / story para argumentos constantes mientras estamos en eso y evitar una desafortunada era de código apestoso. Sí, lo anterior todavía se puede hacer con macros, pero la ergonomía de los genéricos const es mil veces más fácil.

fwiw, así es como me imagino que se vería la versión del argumento const:

#[inline(always)]
fn read_str_digits<B: Bytes>(src: &mut B, min_value: const u32, 
    max_value: const u32, stop_word: const u8, max_digits: u8) -> Result<u32, ()> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="23">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            stop_word => match result {
                min_value..=max_value => break, // valid range
                _ => return Err(()),
            },
            _ => return Err(())
        }
    }

    ...
}

Sería virtualmente idéntico a los genéricos const de no ser por la semántica.

Por lo general, lo movería a una función, excepto que lo siguiente no funcionará porque ciertos valores que se usan en la coincidencia de patrones se han convertido en variables:

Esto parece un caso de uso que se aborda más fácilmente al permitir que los valores de las variables vinculadas se usen en patrones (no solo consts), con alguna sintaxis nueva. El hecho de que los consts estén permitidos actualmente en los patrones es contrario a la intuición y, hasta donde yo sé, histórico.

@mqudsi Perdóname si esta es una pregunta tonta, pero no veo nada malo en el ejemplo que diste. Me parece un caso de uso perfectamente válido para los genéricos const: tener una definición que está generalizada para trabajar con valores arbitrarios como máximo / mínimo. Realmente no veo los beneficios de los argumentos const sobre los genéricos const. Me parecen equivalentes; es decir, los argumentos const podrían implementarse simplemente como un desugaring para const genéricos. ¿Podría dar más detalles sobre lo que está mal con este patrón de diseño?

Aquí hay un resumen del trabajo sobre genéricos const desde la última actualización . La última vez, se trató de la implementación principal: asegurarse de que todo encajara y lograr que se aprobaran algunos de los casos de prueba básicos. Desde entonces, el esfuerzo se ha centrado en hacer que los genéricos const sean más confiables: solucionar los casos que bloquearon el compilador o que no funcionaron inesperadamente o mejorar los diagnósticos. Nos estamos acercando a algo que funciona de manera confiable, aunque todavía queda mucho camino por recorrer.


  • @ skinny121 ha estado haciendo un trabajo fantástico en el último mes solucionando varios problemas pendientes con const genéricos:

    • Ampliación del conjunto de pruebas (https://github.com/rust-lang/rust/pull/60550)

    • Solución de problemas de inferencia de tipos (https://github.com/rust-lang/rust/pull/64679, https://github.com/rust-lang/rust/pull/65579)

    • Admite más tipos de genéricos const, incluidas cadenas y cortes (https://github.com/rust-lang/rust/pull/64858) y punteros (https://github.com/rust-lang/rust/pull/64986 ) (aunque tenga en cuenta que es probable que estos no se estabilicen inmediatamente junto con el resto de genéricos const, ya que no son compatibles con el RFC original )

    • Solucionar problemas de diagnóstico con genéricos const (https://github.com/rust-lang/rust/pull/65154, https://github.com/rust-lang/rust/pull/65579)

    • Solucionar una serie de problemas con el uso de const generics cross-crate (https://github.com/rust-lang/rust/pull/65365)

  • @eddyb solucionó algunos problemas con la evaluación de const que afectaban a los genéricos de const (https://github.com/rust-lang/rust/pull/63497)
  • @matthewjasper solucionó algunos problemas con el uso de genéricos const en macros (https://github.com/rust-lang/rust/pull/63083)
  • @davidtwco solucionó un problema que involucraba const genéricos y constructores (https://github.com/rust-lang/rust/pull/60839)
  • @GuillaumeGomez arregló la visualización de genéricos const en Rustdoc (https://github.com/rust-lang/rust/pull/61605)
  • @varkor solucionó algunos problemas de inferencia de tipos (https://github.com/rust-lang/rust/pull/61570, https://github.com/rust-lang/rust/pull/60742, https: // github. com / rust-lang / rust / pull / 60508), un problema que restringe los lugares donde se pueden usar los genéricos const (https://github.com/rust-lang/rust/pull/60717) y varios bloqueos del compilador (https: / /github.com/rust-lang/rust/pull/61380, https://github.com/rust-lang/rust/pull/61333, https://github.com/rust-lang/rust/pull/60710 )

Además, otro trabajo en el compilador terminó solucionando algunos de los otros problemas relacionados con los genéricos const.

También comenzamos a usar genéricos const dentro del propio compilador: las implementaciones de rasgos de matriz ahora usan genéricos const gracias al trabajo de @ crlf0710 y @scottmcm (https://github.com/rust-lang/rust/pull/60466, https : //github.com/rust-lang/rust/pull/62435), lo que condujo a mejoras en el rendimiento y que también nos permitirá no restringir las implementaciones de rasgos de matriz en el futuro (cuando se estabilicen los genéricos const). @Centril utilizó el mismo enfoque para mejorar VecDeque (https://github.com/rust-lang/rust/pull/63061).


Muchos de los errores particularmente comunes con los genéricos const se han corregido en los últimos meses. @nikomatsakis está investigando la normalización perezosa , que debería solucionar una gran cantidad de los problemas restantes, mientras que @jplatte está buscando corregir la desambiguación de los parámetros const de los parámetros de tipo (por lo que ya no tendrá que escribir {X} para un argumento constante).

También quiero agradecer a @eddyb por toda su tutoría y revisión de genéricos const durante todo el desarrollo, lo cual ha sido invaluable.

Todavía hay muchos otros problemas que abordar y, como antes, podríamos usar toda la ayuda que podamos obtener; si está interesado en abordar cualquiera de los problemas restantes , no dude en enviarme un ping sobre el problema o en Discord o Zulip para consejos sobre cómo empezar.

@mqudsi @ mark-im ¿Sería apropiado extender la alternativa sintáctica actual para genéricos, impl Trait en posición de argumento, a genéricos const? Esta es la sintaxis que @mqudsi sugiere para este caso de uso.

Personalmente, creo que esto mejoraría la legibilidad de tales casos de uso, pero veo un problema: los genéricos normales se utilizan para pasar datos, por lo que están vinculados a un argumento. Este no es el caso de los genéricos const, entonces, ¿cómo los pasaría? ¿Fingir que son argumentos?
En el caso de impl Trait en posición de argumento, tampoco se puede usar con la sintaxis turbofish. Para const en argumento, sería el inverso. Esto puede resultar confuso.

No estoy seguro de si los beneficios superan la "rareza" aquí, pero quería compartir mis pensamientos de todos modos.

Vagamente recordé una discusión sobre esto antes, y ahora la encontré: https://internals.rust-lang.org/t/pre-rfc-const-function-arguments/6709

@ PvdBerg1998 eso parece una muy mala idea. Realmente no entiendo la idea de que <…> sea ​​una _sintaxis difícil_ de leer. Aquí dos posibilidades:

  1. Piensas, como yo, que no es difícil de leer, incluso con varios límites.
  2. Crees que es difícil de leer: entonces, ¿quizás la solución sería ponerlos en una cláusula where ?

Ahora compare con el pre-RFC vinculado anteriormente. Sería introducir un _tag_ (en el sentido del sistema de tipos) directamente en la posición de los argumentos de la función, es decir, enlaces de variables para el cuerpo de una función.

En otros términos: es muy confuso y, como varias personas afirmaron para el rasgo implícito en la posición arg, no es necesario. No cometa el mismo error dos veces para agregar una función que no vale la pena. Especialmente si "finges que son discusiones". El óxido iría en la dirección equivocada aquí. La sintaxis debería ser más ligera, sí, pero no más confusa. Por favor deje de.

@phaazon
Personalmente, estoy totalmente de acuerdo con la propuesta vinculada de que permitir constantes en posición de argumento será un impulso ergonómico realmente bueno. No se trata solo de firmas de funciones (posiblemente las cláusulas where no "resuelven" el problema en absoluto, sino que harán que sea más difícil entender las firmas, ya que debe analizar 3 lugares en lugar de solo uno), sino también sobre cómo se utilizarán esas funciones. Comparar:

let r = _mm_blend_ps::<{2 * C}>(a, b);
let r = _mm_blend_ps(a, b, 2 * C);

La segunda línea es mucho más natural en mi opinión que la segunda. Otro ejemplo sería la creación de matrices: MatrixF32::new(3, 3) frente a MatrixF32::new::<3, 3>() . Estoy totalmente en desacuerdo con que esta funcionalidad sea "muy confusa". Para el usuario, no es más que un requisito adicional para un argumento de función. Ya podemos emular argumentos constantes a través de macros (ver los intrínsecos SIMD dentro de std ), pero es bastante feo e ineficiente.

Con respecto a impl Trait en posición de argumento, inicialmente también estaba en contra de esta característica, pero después de un tiempo cambié de opinión y creo que al final fue una buena decisión. La única parte incompleta en este momento es la interacción con los parámetros de tipo explícitos proporcionados a través de turbofish. Creo que una buena solución sería hacer que los argumentos impl Trait invisibles para turbofish, es decir, los usuarios deberían usarlos cuando estén seguros de que no se necesitará una desambiguación explícita de tipos. Nos permitiría reducir significativamente la necesidad de _ dentro de turbofish en algunos escenarios.

Supongo que una discusión de argumentos const está fuera de tema aquí, por lo que probablemente no debería continuar aquí.

Una cosa que separa los argumentos const argumentos impl Trait (que @ PvdBerg1998 mencionó) es que son argumentos, con todo lo que eso implica para la inferencia de tipos.

No puede pasar un tipo en sí mismo como argumento, pero puede pasar un valor, y dejar que una función infiera sus argumentos genéricos constantes a partir de sus argumentos entre paréntesis normales es completamente razonable.

Esto es bastante común en C ++, por ejemplo:

template <typename T, size_t N>
size_t length(T (&array)[N]) {
    return N;
}

Esto es sutilmente diferente de simplemente aceptar un parámetro const y convertirlo en un parámetro genérico const ( fn foo(const N: usize) -> fn foo<const N: usize>() ). Está más cerca de restringir un parámetro con un parámetro genérico const y luego inferir ese argumento genérico const del argumento. Entonces, el ejemplo de matriz anterior podría verse así:

impl<const W: usize, const H: usize> MatrixF32<W, H> {
    fn new(w: usize<W>, h: usize<H>) -> Self { .. }
}

... donde usize<X> es una sintaxis inventada para "a usize con el valor X ". (De manera análoga, es posible que desee usize<X..Y> para tipos de rango, que se han discutido en el contexto de la generalización de NonZero ).

Si desea evitar la vinculación en tipos de rango, puede buscar en los lenguajes de tipo dependiente que permiten que los parámetros se usen directamente en los tipos, con un poco de ajuste de alcance:

fn new(const W: usize, const H: usize) -> MatrixF32<W, H> { .. }

Ellos no son argumentos. Son genéricos . A nivel de sistema de tipos, eso significa que sus _valores_ viven en tiempo de compilación, mientras que los valores de un argumento de función viven en tiempo de ejecución. Mezclar ambos es extremadamente engañoso.

Como me preocupan los sistemas de tipos, esta propuesta va en contra de muchos principios cuerdos y sólidos. No desea mezclar valores y tipos. Porque, sí, un N (tenga en cuenta que no dije usize , dije N ), aunque piense que es un valor, es mucho más parecido a un tipo que un valor. Como razón fundamental, Haskell no tiene genéricos const pero tiene DataKinds , lo que permite elevar los constructores de datos regulares como tipos:

foo :: Integer -> Integer
foo 0 -- 0 has type Integer

-- but
data P (a :: Integer)

type MyP = P 10 -- 10 has kind Integer, which “value” is the 10 type

Entonces:

fn foo<const X: usize>()

Aquí, X es más parecido a un tipo que a un valor.

Además, me preocupo por la monomorfización. Si leo esto:

let x = foo(12, "Hello, world!", None);

Con su propuesta, si buscamos la definición de foo , es difícil saber qué argumentos monorfizarán foo . Porque cada vez que pasa un valor diferente para un genérico const, crea una función completamente nueva.

Tal vez se sienta más intuitivo para usted y sus razones, pero también tengo razones para decir que no es nada intuitivo en términos de corrección de tipos. Decir que son argumentos es como afirmar que las funciones parametrizadas, en matemáticas, tienen sus parámetros como argumentos. Estás confundiendo parámetros y argumentos, demostrando lo mala que es esa idea.

Es posible que me haya leído mal, dije específicamente " const argumentos ... son argumentos ", no " const genéricos". Mi primer ejemplo de Rust también es exactamente equivalente a DataKinds ( usize<N> es la versión de nivel de tipo de N ). No hay ningún problema aquí en términos de corrección de tipos; no estoy discutiendo en términos de intuición, sino por analogía con los sistemas de tipos existentes, establecidos y de sonido.

En cuanto a su preocupación por la monomorfización, ¡no es diferente a la actual! Cada argumento puede potencialmente causar una nueva monomorfización. También agregaría que la solución a este problema es reducir el costo de todos los genéricos compartiendo código no dependiente entre instancias, incluso, y especialmente, convirtiendo genéricos const en valores de tiempo de ejecución cuando sea posible.

(Y estoy completamente desconcertado por su afirmación de que estoy confundiendo parámetros y argumentos, tuve cuidado de distinguirlos).

Entiendo lo que quieres decir, pero todavía me siento muy preocupado. Debido a que el _argumento_ está etiquetado como _const_, significa que no puede pasarlo _un valor_, hablando en tiempo de ejecución. Debe pasarle un _valor constante_, que se asignará al _const genérico_. Por cierto, no lo mencionó aquí, pero esa es solo una aplicación de un _tipo único_ si considera 10 como un tipo: su valor único posible es 10 .

Como he estado en contra del rasgo implícito en la posición arg, también estoy en contra de ese (y comprenderá por qué podemos comparar aquí). Varios puntos:

  1. No creo que sea necesario, mientras que _const generics_ agrega muchos valores.
  2. Dado que son similares a los tipos, deberían pertenecer al lugar donde se ubican los tipos, no al lugar donde se ubican las variables.
  3. Realmente me gustaría saber por qué algunas personas piensan que <…> es más difícil que (…) , especialmente considerando struct vs fn . Actualmente, struct solo tiene <…> y fn tiene ambos, porque ambos se pueden configurar como parámetros.
  4. Lo que realmente no me gustó de impl Trait en posición arg fue el hecho de que es más difícil ver con qué función se establecen realmente los parámetros. Cuando lee foo<A, B> , sabe que hay dos parámetros que cederán a la definición e implementación de una función.

Siento lo que quieres hacer aquí. foo(1, 3) siente mejor para ti que foo<3>(1) , pero no para mí. La razón de esto es que foo(1, 3) debería aceptar que el segundo argumento esté basado en tiempo de ejecución y la propuesta que usted da lo prohíbe. Sin embargo, puedo ver aplicaciones, especialmente con matrices, pero actualmente no me gusta cómo se ve. Sería mucho más para una solución que dice “si un argumento es const , podemos inferir una variable de tipo / const genérica.

fn foo<const N: usize>(a: usize, b: usize | N);

Esa es solo una sintaxis imaginaria que se me ocurrió para explicar mi punto: si pasas b como un valor constante, N = b y terminas con tu agradable sintaxis:

foo(1, 3)

Si b no es un valor constante, se requiere que pase N explícitamente:

foo<3>(1, x)

Además, cuál sería la sintaxis para esto, con tu propuesta:

fn foo<const N: usize>(x: [f32; N]);
fn new(const W: usize, const H: usize) -> MatrixF32<W, H> { .. }

Nos lleva a cuestionar los ítems asociados, en resumen new() es una función asociada, y debe estar asociada con un tipo concreto, los ítems genéricos const tienen semántica como "Este tipo tiene este parámetro ...", es decir, tipo dependiente. Entonces, new() en el contexto no debería tener ningún argumento. El uso de Explict debería verse como Type<32>::new() . Y, por supuesto, todos los argumentos genéricos const deberían poder evitarse.

Siento lo que quieres hacer aquí. foo (1, 3) se siente mejor para ti que foo <3> (1), pero no para mí. La razón de esto es que foo (1, 3) debería aceptar que el segundo argumento esté basado en tiempo de ejecución y la propuesta que usted da lo prohíbe.

Esa no es mi motivación ni mi intención, no estoy hablando de lo que "se siente mejor" y no creo que la distinción compilación / tiempo de ejecución deba combinarse con la distinción tipo / nivel de valor. Los dos ya están separados: const elementos (valor o fn) ¡ciertamente no son tipos!

A todo lo que me refiero es a la conexión semántica que dibuja la inferencia de tipo entre los parámetros de nivel de valor y los parámetros de nivel de tipo. No hay relación con impl Trait ; mi punto, nuevamente, era distinguir los argumentos constantes de impl Trait , precisamente para evitar este tipo de angustia improductiva.

Además, cuál sería la sintaxis para esto, con tu propuesta:

fn foo<const N: usize>(x: [f32; N]);

¡Esa sintaxis no cambiaría! No estoy proponiendo que eliminemos los parámetros const del nivel de tipo (sintáctica o semánticamente), solo que los agreguemos al nivel de valor, parcialmente para ayudar a la inferencia de los del nivel de tipo.

En cppcon se habló sobre los parámetros de la función constexpr, que parece similar a lo que @ PvdBerg1998 está hablando y parece el hilo interno al que @ mark-im está vinculado. Parece ser bien recibido allí.

Parece una buena idea, pero debería resolverse después de que tengamos la implementación inicial para const-generics hecha

CppCon 2019: David Stone - Eliminación de la metaprogramación de C ++, Parte 1 de N: parámetros de función constexpr

Editar: esto se mencionó en ese hilo, aquí está el documento relacionado para los parámetros de la función constexpr
https://github.com/davidstone/isocpp/blob/master/constexpr-parameters.md

Me gustaría solicitar que nos reservemos la discusión de un cambio sintáctico tan significativo para un hilo en los aspectos internos o un problema (o enmendar las relaciones públicas) en el repositorio de RFC. La sección de comentarios de este problema ya es bastante larga y la sintaxis en cuestión parece innecesaria para un MVP utilizable de genéricos const.

Si pudiéramos mover los comentarios, probablemente lo haríamos. ¿Podríamos querer bloquear el problema a los miembros del equipo @ rust-lang y ocultar todos los comentarios fuera del tema?

Toda la discusión del diseño debe ocurrir en el repositorio de RFC y todos los informes de errores deben estar en números separados.

cc @ rust-lang / moderation ¿Existe un precedente para hacer esto?

Puede _convertir_ un comentario en un problema, pero no mover comentarios. El bloqueo está bien y simplemente me escondería de los comentarios del tema.

Con el comienzo del nuevo año, pensé que sería bueno dar otra actualización sobre dónde estamos ahora con los genéricos const. Han pasado un poco más de 2 años desde que se aceptó la RFC const genéricos . En el primer año (aproximadamente 2018), se llevaron a cabo una serie de grandes esfuerzos de refactorización para facilitar los genéricos const mejorando el manejo de los parámetros genéricos en general en todo el compilador. En el segundo año (aproximadamente 2019), se comenzó a trabajar en la implementación de los genéricos const en sí: primero haciendo que funcione lo mínimo, y luego disminuyendo la velocidad para mejorar la integridad de la función. La gente también comenzó a experimentar con el uso de genéricos const en el propio compilador. En las dos primeras publicaciones de actualización se encuentran descripciones más detalladas de estos esfuerzos: [1] , [2] .


Ha habido un buen progreso en los últimos meses desde la última actualización.

  • @ skinny121 :

    • Se solucionó un problema que rompía los genéricos const en la compilación incremental y entre cajas (https://github.com/rust-lang/rust/pull/65652)

    • Se corrigió un error con la inferencia de tipo que afectaba a los genéricos const en los diagnósticos (https://github.com/rust-lang/rust/pull/65579)

    • Refactorizó la interfaz para la evaluación constante (https://github.com/rust-lang/rust/pull/66877)

  • @yodaldevoid implementó la desambiguación de los parámetros de tipo y const para que los parámetros de const ya no necesiten estar envueltos en {} (https://github.com/rust-lang/rust/pull/66104)
  • @eddyb corrigió el uso de genéricos const como longitudes de matriz, una vez que se implementa la normalización perezosa (https://github.com/rust-lang/rust/pull/66883)
  • @varkor :

    • Se implementó la verificación structural_match para prohibir que los tipos con verificación de igualdad personalizada se utilicen como genéricos const (https://github.com/rust-lang/rust/pull/65627)

    • Se corrigieron algunos diagnósticos para tener en cuenta los genéricos const (https://github.com/rust-lang/rust/pull/65614)

    • Realicé varias refactorizaciones y arreglos de inferencia de tipos (https://github.com/rust-lang/rust/pull/65643, https://github.com/rust-lang/rust/pull/65660, https: // github. com / rust-lang / rust / pull / 65696)

¡Un gran agradecimiento a todos los que han estado ayudando con los genéricos const!


¿Que sigue? El mayor bloqueador de genéricos const en este momento es la normalización diferida , que es necesaria para ciertos tipos de límites genéricos const (como en matrices ). @ skinny121 actualmente está

Dejando a un lado la normalización perezosa, todavía hay una cantidad considerable de errores y recortes de papel que nos gustaría abordar. Si está interesado en abordar cualquiera de los problemas restantes, no dude en enviarme un ping sobre el problema o en Discord o Zulip para obtener sugerencias sobre cómo comenzar. ¡Estoy seguro de que podemos avanzar bien en 2020 y, con suerte, acercarnos a un punto en el que la estabilización se convierta en una discusión viable!

¿Existe un subconjunto de genéricos const que no alcanza la normalización perezosa y no tiene muchos errores y cortes de papel, pero que puede expresar una buena cantidad de código útil? Noto que las implicaciones de std de muchos rasgos para matrices parecen funcionar bien. ¿Quizás hay un estrechamiento que permitiría a otras cajas escribir el tipo de implicaciones que tenemos en std para sus propios rasgos, aunque no admitan todas las funciones más sofisticadas?

Ahora estamos a más de la mitad del año, así que resumiré brevemente el trabajo que se ha estado realizando. Ha habido muchas personas involucradas en la mejora del soporte de genéricos const en los últimos 6 meses, ¡así que gracias a todos los que han ayudado de alguna manera! Quiero agradecer especialmente a @ skinny121 y @lcnr , quienes han abordado algunas características importantes de las que los genéricos const han estado faltando por un tiempo: normalización perezosa para constantes y soporte de argumentos const en llamadas a métodos , así como corregir numerosos errores difíciles. Sus esfuerzos son evidentes en el resumen a continuación.

Los genéricos const ahora se utilizan en varios lugares a lo largo del compilador, y ya hay experimentación con rasgos y funciones que utilizan genéricos const, por ejemplo, slice::array_chunks y IntoIterator y FromIterator por [T; N] (https://github.com/rust-lang/rust/pull/65819, https://github.com/rust-lang/rust/pull/69985). La restricción de longitud 32 para implementaciones de rasgos de matriz también se está preparando finalmente

Actualmente estamos discutiendo la estabilización de un subconjunto de genéricos const que deberían cubrir una amplia gama de casos de uso (aunque todavía quedan algunos problemas por resolver antes de que los genéricos const se puedan estabilizar por completo).


  • @ skinny121 :

    • Implementó la primera versión de normalización perezosa (https://github.com/rust-lang/rust/pull/68505, https://github.com/rust-lang/rust/pull/69181, https: // github. com / rust-lang / rust / pull / 67717, https://github.com/rust-lang/rust/pull/67890)

    • Rendimiento mejorado (https://github.com/rust-lang/rust/pull/68118)

    • Se corrigieron varios errores (https://github.com/rust-lang/rust/pull/68143)

  • @lcnr :

    • Se agregó soporte para genéricos const explícitos en argumentos de tipo para métodos (https://github.com/rust-lang/rust/pull/74113)

    • Completó la primera implementación de normalización perezosa para constantes (https://github.com/rust-lang/rust/pull/71973) con @ skinny121

    • Se agregó una pelusa por unused_braces (https://github.com/rust-lang/rust/pull/70081)

    • Se solucionó el problema con los genéricos const en los patrones (https://github.com/rust-lang/rust/pull/70562)

    • Prohibido dyn Trait para parámetros genéricos const (https://github.com/rust-lang/rust/pull/71038)

    • Impresión bonita mejorada (https://github.com/rust-lang/rust/pull/72052)

    • Se corrigieron varios errores (https://github.com/rust-lang/rust/pull/70032, https://github.com/rust-lang/rust/pull/70107, https://github.com/rust- lang / rust / pull / 70223, https://github.com/rust-lang/rust/pull/70249, https://github.com/rust-lang/rust/pull/70284, https: // github. com / rust-lang / rust / pull / 70319, https://github.com/rust-lang/rust/pull/70541, https://github.com/rust-lang/rust/pull/72066, https: //github.com/rust-lang/rust/pull/74159)

    • Refactorización (https://github.com/rust-lang/rust/pull/70478)

    • Pruebas agregadas (https://github.com/rust-lang/rust/pull/74392, https://github.com/rust-lang/rust/pull/74445)

  • @eddyb :

    • Se solucionaron problemas con los genéricos const en longitudes de matriz (https://github.com/rust-lang/rust/pull/70452)

    • Impresión bonita mejorada (https://github.com/rust-lang/rust/pull/71232)

    • Manejo de errores mejorado (https://github.com/rust-lang/rust/pull/71049)

    • Refactorización (https://github.com/rust-lang/rust/pull/70164)

  • @ Aaron1011 : error corregido con el uso de genéricos const entre cajas (https://github.com/rust-lang/rust/pull/72600)
  • @petrochenkov : problemas solucionados con la resolución de nombres y genéricos const (https://github.com/rust-lang/rust/pull/70006)
  • @yodaldevoid : solucionó un problema con el uso de por vida en los parámetros genéricos const (https://github.com/rust-lang/rust/pull/74051)
  • @contrun : solucionó un ICE (https://github.com/rust-lang/rust/pull/69859)
  • @ oli-obk: refactorización (https://github.com/rust-lang/rust/pull/69981)
  • @Centril : diagnóstico mejorado (https://github.com/rust-lang/rust/pull/70261)
  • @DutchGhost : agregó una prueba (https://github.com/rust-lang/rust/pull/70539)
  • @varkor :

    • Impresión bonita mejorada (https://github.com/rust-lang/rust/pull/67909, https://github.com/rust-lang/rust/pull/68111)

    • Se corrigieron varios errores (https://github.com/rust-lang/rust/pull/67906, https://github.com/rust-lang/rust/pull/68388, https://github.com/rust- lang / herrumbre / tirar / 68434)

    • Diagnósticos mejorados (https://github.com/rust-lang/rust/pull/70845)

    • Pruebas agregadas (https://github.com/rust-lang/rust/pull/68312, https://github.com/rust-lang/rust/pull/70939)


Si bien los genéricos const ya han recorrido un largo camino, todavía hay errores y bordes afilados . Si está interesado en abordar cualquiera de los problemas restantes, no dude en enviarme un ping a mí, @lcnr o @eddyb sobre el problema, o en Zulip, para obtener sugerencias sobre cómo comenzar. ¡Gracias!

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