Rust: Rastreamento de problema para genéricos const (RFC 2000)

Criado em 15 set. 2017  ·  202Comentários  ·  Fonte: rust-lang/rust

Rastreamento de problema para rust-lang / rfcs # 2000

Atualizações:

Se você quiser ajudar, dê uma olhada nas questões abertas de genéricos e sinta-se à vontade para enviar ping em @eddyb , @yodaldevoid , @oli-obk ou @lcnr para obter ajuda para começar!


Estabilização de bloqueio:

  • [ ] Projeto:

    • [] Resolvendo a ordem dos parâmetros const e type, com parâmetros padrão

    • [] Decida qual é o melhor equilíbrio de custo de UX / implementação para unificar expressões const abstratas.

    • [] Como determinamos a boa formação de expressões const.

  • [x] Implementação
  • [] Documentação

    • [] guia rustc


Problemas de implementação restantes:

  • [] Resolva vários comentários de FIXME(const_generics) .
  • [] Resolva problemas com canonicalização / normalização preguiçosa.
  • [] Investigue a manipulação de parâmetros const em padrões.
  • [] Adicione mais testes.
  • [] Implementar padrões para parâmetros const ( FIXME(const_generics:defaults) ).
  • [] Corrija outros problemas A-const-genéricos .
  • [] Auditar usos de has_infer_types .
  • [x] Proíbe expressões complexas para argumentos const envolvendo parâmetros (por enquanto), por exemplo, {X * 2} .
  • [] Diagnóstico de auditoria (por exemplo, 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

Comentários muito úteis

Aqui está um resumo do progresso até agora nos genéricos Const.


Antes que o trabalho com os genéricos const pudesse começar corretamente, havia alguma refatoração que precisava ser feita. @jplatte assumiu a tarefa com https://github.com/rust-lang/rust/pull/45930. @jplatte então começou a trabalhar na implementação principal dos genéricos const, mas descobriu que não tinha tempo suficiente para continuar.

@yodaldevoid e eu continuamos de onde @jplatte parou, mas rapidamente descobrimos que o progresso era dificultado pela maneira como os parâmetros genéricos eram tratados em geral. Assim, começou uma série de mudanças para renovar a maneira como os genéricos eram tratados em toda a base de código: 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.

Feito isso, a implementação dos genéricos const poderia começar para valer. Desde então, @yodaldevoid e eu, lenta mas seguramente, adicionamos suporte 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 e mais recentemente https://github.com/rust-lang/rust/pull/59008. (Eles foram divididos principalmente da solicitação principal const generics pull .)


Qual é o status agora? Alguns testes de genéricos const agora funcionam 🎉 No entanto, ainda existem aqueles que não funcionam, e há uma série de FIXME s em todo o código que ainda temos que resolver. Estamos chegando perto agora, porém, e estamos em um estágio em que há algumas frutas ao alcance da mão, se você quiser ajudar.

  • Primeiro, precisamos de mais testes. Existem apenas alguns testes de genéricos Const até agora, mas gostaríamos de muitos mais. No momento, como estamos cientes de uma série de problemas, não precisamos de relatórios de bug, mas precisamos passar nos testes. Se você criar um caso de teste que funcione (e não seja muito semelhante a um teste existente), sinta-se à vontade para abrir uma solicitação pull para adicioná-lo.
  • Em segundo lugar, como mencionado, há uma série de FIXME(const_generics) espalhados por todo o código. Estamos planejando trabalhar nosso caminho através deles, mas @yodaldevoid e eu não temos muito tempo, então se você acha que pode resolver um, vá em frente (embora você possa querer deixar um comentário dizendo isso, então nós não fazemos t esforços duplicados).

Eu escrevi uma visão geral de alguns dos problemas de implementação restantes antes que const generics esteja pronto para o teste adequado, na postagem superior, para dar uma ideia aproximada do que resta fazer.

Demorou, mas temos feito progressos constantes e esperamos manter o ritmo. É motivador finalmente ver as coisas começando a se encaixar!

Todos 202 comentários

44275 adicionou um predicado ConstEvaluatable , e WF([T; expr]) agora requer ConstEvaluatable(expr) , já que expr é avaliado preguiçosamente (mesmo se for apenas um literal inteiro). Preencher este predicado requer que a expressão seja avaliada com sucesso, enquanto a normalização ignora o erro e simplesmente deixa a expressão Unevaluated que encontrou, intacta, que é mais ou menos o que acontece com as projeções de tipo associadas. Espero que o mesmo sistema seja escalonado para constar de genéricos.

@EpicatSupercell expressou interesse em trabalhar nisso. Eu os orientarei durante a implementação inicial. No entanto, não podemos ir muito longe por causa das limitações descritas em # 44275.

Ou seja, precisamos da normalização preguiçosa de @nikomatsakis para permitir que expressões constantes embutidas em tipos observem os limites no escopo (da função / definição de tipo / impl / etc. item em que estão), sem produzir dependências cíclicas na metade do tempo.

Pontos de referência de implementação (para orientação mais direta, busque @eddyb no Gitter ou eddyb no IRC):

  • Declaração / sintaxe:
  • Declaração / Semântica

    • Estruturas de dados: ty::Generics - adicione const parâmetros ao lado dos de tipo

    • Conversão de HIR: generics_of - crie ty::ConstParameterDef partir de hir::ConstParam

  • Resolução de uso / nome

    • Estruturas de dados: Def - adicione uma variante para const parâmetros

    • Passagem de resolução de nome: with_type_parameter_rib - suporta o tipo e const genéricos

  • Uso / Semântica
  • Inferência

Observe que tudo isso deve permitir impl<T, const N: usize> Trait for [T; N] {...} , mas não realmente passar uma expressão constante para um tipo / função, por exemplo, ArrayVec<T, 3> .

Eu gostaria de dar uma olhada nisso :)

@jplatte @eddyb Alguma notícia sobre isso?

@samsartor havia uma refatoração que precisava ser feita antes do trabalho principal de implementação. Isso agora está quase pronto (estou aguardando comentários no momento). Na verdade, não sei quanto trabalho resta depois disso. Analisar parâmetros const foi o que eu comecei inicialmente, antes da refatoração. Não está feito, mas devo ser capaz de fazê-lo relativamente em breve.

@jplatte Seria bom se você pudesse vincular a solicitação pull. Eu não fui capaz de encontrar.

Não há RP ainda. Todo o meu trabalho pode ser encontrado aqui .

Bom trabalho até agora @jplatte 🍻

Agora há um PR para a base ( Generics refatoração): # 45930

Acho que isso já está incluído, mas seria bom lidar com a dobra de estilo C ++ para lidar com matrizes dimensionais de estilo n de álgebra linear com comprimentos variados, ou seja, uma matriz 4x4 [[f64; 4]; 4] ou 4x3x5x6 dimensional [[[[ f64; 6]; 5]; 3]; 4] e ser capaz de agrupar e gerar métodos especializados para ele E implementações de características adequadas para escalares dimensionados de vetores corretamente, etc. Consulte https://gist.github.com/huhlig / 8b21850b54a75254be4b093551f8c2cb para um exemplo rudamentar.

Não me lembro de ninguém ter proposto expressões de dobra para Rust antes, muito menos como parte desta RFC. Mas, uma vez que se trata de Rust, há algum motivo para não implementarmos expressões de dobra como uma macro comum, em vez de uma nova sintaxe dedicada?

Quais são as próximas etapas agora que o # 45930 foi mesclado? @jplatte

@kjaleshire A próxima etapa é implementar a análise de genéricos const (veja o comentário de @eddyb mais adiante). Já comecei a trabalhar nisso antes de ficar claro que a refatoração na refatoração seria necessária. Meu trabalho existente sobre isso pode ser encontrado aqui ; ele ainda não foi rebaseado desde que o PR de refatoração foi mesclado.

@jplatte Acredito que você quisesse mencionar @kjetilkjeka.

Obrigado pela atualização! Tenho certeza de que estou longe de ser o único ansioso por esse recurso. Mantenha o bom trabalho!

@jplatte não quer ficar impaciente, mas houve algum trabalho nisso? Você precisa de ajuda? Alguém deveria ajudar?

@ est31 Desculpe. Faz algum tempo que não tenho tempo para trabalhar nisso e não tenho certeza de quando terei tempo. Talvez seja melhor para outra pessoa continuar de onde parei. Acho que a parte de análise está quase concluída. Existem dois lugares no código onde eu não tinha certeza do que fazer no caso de um parâmetro genérico ser um parâmetro const:

Também não há nenhum teste para a análise de genéricos const ainda (e para o código de impressão bonita que atualizei ao mesmo tempo). Não tenho certeza de quais outras informações posso fornecer que seriam necessárias / úteis para outra pessoa continuar trabalhando nisso, mas sinta-se à vontade para me enviar um ping se algo não estiver claro sobre meu código.

@jplatte ~ None no primeiro e nada no segundo ~~ (cc @pnkfelix @nikomatsakis)

EDIT : nada a fazer em ambos os casos.

@eddyb Para o primeiro snippet vinculado, você tem certeza de que None deve ser retornado? Posso não entender o que está acontecendo aqui, mas me parece que nada deve ser feito. Se None for retornado, outros parâmetros que podem ser inseguros serão ignorados.

@yodaldevoid Desculpe, você está certo, não sabia que isso era em Generics .

Eu gostaria de continuar onde @jplatte parou. Estou trabalhando nisso há alguns dias, seguindo as instruções de mentoria de @eddyb porque tive tempo apenas para ver se conseguia chegar a algum lugar. Eu desci para a seção "Uso / Semântica" neste ponto. Provavelmente irei passar por um dos bate-papos em breve para fazer perguntas.

@yodaldevoid Ótimo para ouvir. Eu não sei sobre o Gitter, mas você definitivamente terá muitas orientações sobre #rustc e / ou # rust-internals no IRC!

@yodaldevoid : você gostaria de alguma colaboração? Também tenho investigado esse problema (😅) - talvez possamos preencher as lacunas no trabalho um do outro. (Você pode ver o que fiz até agora aqui .) Talvez possamos bater um papo no IRC (#rustc ou # rust-internals) sobre isso?

@varkor Parece que você está mais adiantado do que eu. Certamente estaria disposto a colaborar. Vou tentar pegar você no IRC em algum momento e, nesse meio tempo, ver se fiz algo que você ainda não fez.

Existe algum progresso nisso?
Estou escrevendo código para incorporado e realmente preciso de genéricos const.

@ qwerty19106
Progresso está sendo feito nisso, embora lentamente. @varkor e eu temos trabalhado nisso de vez em quando como tínhamos tempo. Fiz alguns progressos esta semana e estamos vendo a luz no fim do túnel para uso básico.

Além de apenas implementar genéricos const, nós (varkor) fizemos uma limpeza para tornar tudo isso possível (consulte # 48149 e # 48523). Acredito que o plano atual é esperar que essas duas solicitações de pull sejam processadas antes de inserir os genéricos const, mas o Varkor pode falar mais sobre isso.

Eu realmente entendo sua necessidade de genéricos const para trabalhos incorporados. Comecei com isso porque eu também realmente quero genéricos constantes para que possa limpar e tornar seguras grandes faixas de código incorporado.

TL; DR: O progresso está acontecendo, mas isso é complexo. Eu sinto você na frente embutida.

Para a caixa de seleção "documentação", seria ótimo obter algo no guia rustc .

Obrigado @yodaldevoid por sua resposta. Aguardo ansiosamente o fim do seu trabalho.

Atualização para os curiosos (já que eu também era curioso). Re: os dois PRs mencionados acima : # 48523 foi mesclado e # 48149 está progredindo constantemente.

@ mark-im Good stuff! Bom trabalho de @varkor. Quando é o ETA mais ou menos, você sabe? :-)

Saudações, @ flip111.

Parece que a segunda grande refatoração PR # 48149 foi mesclada :)

/ me cc

Próximo @varkor PR # 51880

Só queria dar uma rápida atualização, como sei que algumas pessoas têm perguntado sobre o progresso com os genéricos const.

Para dar um contexto, em março , @yodaldevoid e eu tínhamos uma implementação inicial que estava quase funcionando (a maior parte da implementação parecia estar concluída e estávamos apenas limpando alguns travamentos restantes). No entanto, o ramo em que estávamos trabalhando era pré-miri. Quando o miri foi mesclado (e em uma série de PRs subsequentes), o código para lidar com a avaliação constante mudou significativamente, o que significa que muito do que tínhamos feito ficou desatualizado.

Além disso, foi decidido que o código dos parâmetros genéricos precisava ser limpo em geral antes de adicionar genéricos const, tanto para melhorar a legibilidade, mas também para cometer erros em que esquecemos de lidar com genéricos const em um determinado caso mais difícil de faço. Isso foi feito em https://github.com/rust-lang/rust/pull/48523 , https://github.com/rust-lang/rust/pull/48149 e será concluído em https: // github. com / rust-lang / rust / pull / 51880. Eles foram um pouco mais envolvidos do que eu esperava inicialmente e levaram um pouco mais de tempo para avançar do que o estimado.

Nesse ínterim, @yodaldevoid e eu estamos trabalhando para tornar nossa implementação original compatível com todas as alterações subsequentes no rustc. Demorou, mas estamos chegando lá (embora haja o problema perene de nunca ter tanto tempo quanto você esperava). Espero que tenhamos boas notícias em breve sobre esse assunto. (Enquanto isso, https://github.com/rust-lang-nursery/chalk tem feito um bom progresso, o que deve resolver algumas das dificuldades descritas originalmente por @eddyb .)


Algumas pessoas perguntaram como podem ajudar: Acho que, neste estágio, será mais fácil tentar terminar a implementação inicial e ver quais partes precisam de atenção. Quando estiver pronto, vamos precisar de muitos testes, utilizando genéricos const em diferentes lugares (incluindo os inválidos, para mensagens de erro), então esse é definitivamente um lugar que poderíamos fazer com muita ajuda! Avisaremos você quando isso acontecer!

Desculpe se não é o lugar apropriado para isso, mas tenho uma sugestão a respeito da igualdade de expressões const abstratas. Em geral, reduz-se diretamente a uma tipificação totalmente dependente e a um território indecidível. No entanto, na ferrugem tudo é eventualmente instanciado com valores / tipos concretos, então podemos afirmar algumas igualdades e adiar sua verificação para instâncias monomórficas.

O que quero dizer é que uma maneira relativamente simples de verificar a igualdade de expressões const abstratas é a seguinte:

  • lida automaticamente com igualdade sintática (ou seja, N+1 == N+1 deve funcionar fora da caixa)
  • permitir ao usuário, no momento da definição, adicionar equações como N+M == M+N (talvez na cláusula where ?). Essas equações podem ser usadas pela verificação de igualdade (usando alguma forma de fechamento de congruência). Uma definição que não digite verificação usando essas equações fornecidas é rejeitada.
  • em um ponto de expansão monomórfico, todas as equações podem ser verificadas calculando as exprs const, que não são mais abstratas. Aqui há uma escolha de design: se uma equação que se reduz a false , pode haver um erro de compilação ("equações são axiomas") ou a característica não pode ser instanciada ("equações são restrições")
  • em um ponto de expansão parametrizado, essas equações são transportadas: se você tiver uma função f parametrizada por N onde N+0==N , esta equação não deve ser perdida em um chamador g visto que terá de ser verificado em cada lugar monomórfico onde g é chamado.

Os prós deste método são que não há necessidade de um provador de teorema, solucionador SMT ou reescritor aritmético no próprio rustc. "Apenas" uma verificação de igualdade sintática de tipos, aliases de módulo e módulo um conjunto de equações fornecidas pelo usuário.
O contras é que é mais manual para o usuário, uma vez que igualdades aparentemente óbvias como M+N=N+M devem ser adicionadas explicitamente.

Exemplo:

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

Equações como axiomas

Isso significa que e1 == e2 deve ser sempre verdadeiro, então não é realmente uma restrição where , mas sim uma assert_eq!(e1,e2) que pode ser usada pelo verificador de tipo. Aqui, o implementador faz uma promessa de que isso sempre é verdade e expõe seus usuários a erros de compilação se eles fornecerem parâmetros que refutem a equação.

Equações como restrições

Aqui, a cláusula where e1 == e2 é uma restrição que deve ser satisfeita para o uso bem-sucedido desse traço / função parametrizado. Isso significa que e1 == e2 não precisa ser sempre válido, o que pode ser interessante para equações verdadeiras apenas sobre um domínio, como (x*y) / z == (y*x) / z que não seria capaz de instanciar z==0 .

@ c-cube Já temos um sistema para "cláusulas where implícitas", pelo menos para funções, derivadas de "regras de WF (boa formação)", ou seja, se você definir um fn foo<'a, 'b>(x: &'a &'b i32) {} então requer que os chamadores satisfaçam 'b: 'a (como resultado da necessidade de WF(&'a &'b i32) -> &'b i32: 'a -> 'b: 'a ).

Podemos reutilizar esse sistema (na verdade, já está implementado) para empurrar as restrições fornecidas pela assinatura (da forma "esta expressão constante foi avaliada com êxito") até os chamadores, enquanto qualquer coisa usada apenas dentro do corpo ainda precisaria de where cláusulas

Quase tudo o mais que você está descrevendo parece próximo do planejado (incluindo uma forma de "igualdade sintática"), mas não queremos verificações de "tempo de monomorfização", podemos ter maneiras (implícitas ou por meio de where cláusulas) para "propagar restrições para instanciadores", e então nós produzimos erros onde as restrições são "ainda muito genéricas" (tão desconhecidas se elas valem), ou caso contrário, se podemos avaliá-las e elas não acontecem.

Quase tudo o mais que você está descrevendo parece próximo do planejado

@eddyb , você conhece algum post de referência que discuta esses planos?

@ flip111 Provavelmente alguns dos RFCs. Talvez @withoutboats saiba melhor.

# 51880 está pronto: tada :: tada:

@withoutboats @ oli-obk O que você acha sobre bloquear a unificação adequada de expressões como N + 1 (por exemplo, [T; N + 1] ) em obter alguma forma básica de avaliação simbólica em miri ?

Acho que já teríamos um mecanismo para codificá-lo, quando @varkor adiciona ConstValue::{Infer,Param} , podemos usar isso para rastrear valores "(superficialmente) válidos, mas desconhecidos" (simbólicos) e, em seguida, também incluir o valor (principalmente inteiros) operações e chamadas além disso.

Quaisquer decisões de fluxo de controle não assert ainda exigiriam valores conhecidos, mas o próprio assert poderia talvez ser reificado como AssertThen(condition, assert message, success value) .
(Ambos condition e success value podem ser simbólicos!)

Ser capaz de exportar os valores simbólicos para ty::Const significa que podemos implementar algumas das unificações fora do miri, tratando (a maioria?) Das operações como opacas.

Temos que ter cuidado para não assumir nada de variáveis ​​de inferência, mas por exemplo N + 1 acho que podemos apoiar a unificação de dois Add(Param(N), Bits(1)) juntos.

@varkor Um caso de teste que acho que deveria funcionar apenas com normalização preguiçosa, sem unificação:

/*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()
}

Isso só funcionaria porque:

  • N - 1 aparece no nível de tipo exatamente uma vez

    • tudo pode se referir a essa expressão, de forma opaca

    • ( possível solução alternativa: type ArrayWithoutLast<T, const N: usize> = [T; N - 1]; )

  • está em uma posição de argumento dentro da assinatura

    • (não me lembro se a posição de retorno também funcionaria. @nikomatsakis?)

    • chamadores podem provar que é WF, fornecendo valores concretos para N

    • "borbulhar" o requisito WF precisa de unificação / ArrayWithoutLast

    • outros usos precisariam "embutir em uma cláusula where ", por exemplo [T; N - 1]: Sized



      • pode até abusar de where [T; N - 1]: (isso será ignorado hoje? ai!)


      • novamente ignore a unificação com where ArrayWithoutLast<T, N>: ...



Portanto, no geral, podemos provavelmente confiar nas regras do WF para forçar a "validação" de expressões const para os usuários, mas ainda queremos a unificação para outras coisas (incluindo a não necessidade de hacks como ArrayWithoutLast ).

53645

Pequena questão que se origina da discussão das Unidades de Medida. O que devemos fazer se o tipo genérico sobre constante não depende diretamente dele? Por exemplo:

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

_phantom: [(), N],

Suponho que você quis dizer [(); N] , mas isso só funcionará para usize .

Eu acho que faria sentido ter um marker::PhantomConst permitindo qualquer tipo const .

Hmm… Relendo a RFC com os comentários mais recentes em mente, fico pensando: algo como isso seria permitido?

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

Não consigo ver isso sendo banido em lugar nenhum, mas teria pensado que merecia pelo menos um exemplo.

@Ekleog : pelo que eu sei, os tipos de parâmetro const podem não depender dos parâmetros de tipo inicialmente (embora definitivamente faça sentido para uma extensão). (A implementação é mais complicada se permitirmos isso, então faz sentido começar com a versão mais simples.)

Como está o progresso nisso? Sabemos uma hora aproximada em que isso deve acontecer todas as noites?

@Zauberklavier Assim que esta solicitação de pull for concluída. Para citar:

Há um longo caminho a percorrer

@newpavlov Presumi que parâmetros constantes seriam permitidos sem serem usados ​​em campos.
O motivo pelo qual os parâmetros de tipo devem ser usados ​​é por causa da variação, mas as constantes não têm esse problema.

@eddyb também é o que me lembro da discussão RFC.

@varkor Então isso significa que PhantomConst proposto por @cuviper não pode existir no estado atual desta RFC ... certo? embora os comentários mais recentes pareçam apontar que não há necessidade de PhantomConst qualquer maneira.

Os parâmetros const afetam a variação dos parâmetros de tipo que eles envolvem?

Atualmente, acredito que os genéricos const não podem ter parâmetros de tipo em seu tipo.

Não estou certo sobre o que a primeira versão funcional disso será capaz de fazer.

Por exemplo, se o código neste comentário seria compilado:
https://github.com/rust-lang/rfcs/pull/2581#discussion_r230043717

Se esse código compilasse, seria uma maneira de impor restrições para constantes antes de obter uma sintaxe para ele.

@ rodrimati1992 Você provavelmente pode fazer (): IsTrue<{N < 128}> sem um tipo separado.
Eu acho que você deseja reutilizar a restrição e fazê-la realmente funcionar? (porque copiar a expressão N < 128 não fará com que funcione, inicialmente)
Você poderia usar trait Lt128<const N: usize> = IsTrue<{N < 128}>; , eu acho.
Também há a opção de apenas escrever where [(); 128 - N], , eu acho (mas não tenho certeza se podemos garantir que isso entre em pânico).

@ rodrimati1992 Você provavelmente pode fazer (): IsTrue<{N < 128}> sem um tipo separado.
Eu acho que você deseja reutilizar a restrição e fazê-la realmente funcionar? (porque copiar a expressão N < 128 não fará com que funcione, inicialmente)
Você poderia usar trait Lt128<const N: usize> = IsTrue<{N < 128}>; , eu acho.
Também há a opção de apenas escrever where [(); 128 - N], , eu acho (mas não tenho certeza se podemos garantir que isso entre em pânico).

Então, com apelidos de trait que eu poderia reescrever é para este ?:

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

A ideia com o Asserttrait é usar um erro de tipo para imprimir uma mensagem de erro incorporada em um tipo, que no caso de AssertLessThan128 é:

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

que espero ser mais útil do que where [(); 128 - N], , uma vez que diz a você na mensagem de erro porque o erro de tempo de compilação aconteceu.

Você também pode fazer if !(N < 128) { panic!("...") } em uma constante, eu acho?

Achei que a arquitetura std :: fmt não estaria lá até que const trait constraints (ou algo semelhante) chegassem.

Com isso, você pode ter mensagens de erro com vários valores (embutidos em tipos).

Talvez seja melhor esperar que const generics fale sobre isso, pois será mais fácil mostrar exemplos executáveis.

@ rodrimati1992 veja https://github.com/rust-lang/rfcs/blob/master/text/2345-const-panic.md , haverá um caso especial para obter const panic!() antes que isso acontecesse ser possível por meio de outros recursos (em um palpite, provavelmente, não permitirá a formatação personalizada de valores no início).

@ rodrimati1992 Ahh, entendo, esse é mesmo um truque inteligente!

Qual é a situação disso?

A próxima etapa ainda é https://github.com/rust-lang/rust/pull/53645 AFAIK.

Isto está correto. Para dar uma atualização rápida para aqueles que não seguem o PR # 53645, const generics compilou e funciona em pelo menos um caso de uso simples. O que resta é terminar o codegen para outros casos de uso, incluindo genéricos constantes em arrys e alguma limpeza de saída de erro. Depois disso, o PR deve estar pronto e mesclado e as pessoas podem começar a brincar com ele.

Pode estar fora do tópico, mas isso permitirá que uma variante ou fork de Chunks e métodos relacionados tenha Item um array de tamanho fixo em tempo de compilação em vez de um slice? Isso permitiria que um loop for se desestruturasse / vinculasse a um padrão irrefutável que mapeou os elementos no bloco para variáveis, como for (first, second) in arr.chunks(2) que atualmente falha, e estou supondo (sem qualquer justificativa reconhecidamente) que permitiria mais otimização em certos casos de uso.

@jeffvandyke Você pode estar pensando especificamente em ChunksExact . Essa mudança seria uma quebra de API, então não pode ser feita. Poderíamos adicionar novas APIs que forneçam referências de array no futuro, mas não podemos quebrar as existentes.

@jeffvandyke Você pode estar pensando especificamente em ChunksExact . Essa mudança seria uma quebra de API, então não pode ser feita. Poderíamos adicionar novas APIs que forneçam referências de array no futuro, mas não podemos quebrar as existentes.

Estou apenas esperando que isso seja implementado e estabilizado antes de propor um PR que adiciona essa API além de ChunksExact e suas variantes :)

As variantes de tempo de execução e de tempo de compilação têm seus casos de uso, você nem sempre sabe o tamanho do bloco com antecedência. Em termos de otimização, se você usar ChunksExact com uma constante, deve ser mais ou menos o mesmo de acordo com meus testes. O compilador pode otimizar todas as verificações de limites.

a ser implementado e estabilizado

Eu sugeriria não esperar pela estabilização, pois tal API seria mais um bom uso para ajudar a exercitar esse recurso para estabilização.

Estou supondo que isso ainda não funciona para blocos de impl ? eu tentei

#![feature(const_generics)]

struct The<const Val: u64>();

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

mas, como de fato foi avisado, o compilador travou com

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

O erro está relacionado ao const em impl<const Val: u64> pois a remoção dessa parte causa outros erros, mas não trava.

mas adereços para Rust por sequer considerar esse recurso. Eu não tinha ideia se funcionaria, mas a sintaxe parecia natural, eu fui em frente, e rustc disse que existia :)

Não estou surpreso que não esteja funcionando, já que este recurso muito antecipado ainda está sendo examinado pelo compilador enquanto falamos (veja, por exemplo, # 59008 e # 58581 e o trabalho anterior em # 53645 que foi abandonado porque o PR era muito grande , mas ainda mantido aberto como um rastreador para anunciar o progresso).

No entanto, não tenho certeza se os acessos de slice fora dos limites devem ser esperados dos stubs de implementação atuais. @varkor @yodaldevoid , pode dar uma olhada?

Sim, o aviso está correto: os genéricos const ainda não são funcionais de nenhuma forma. Ainda existem mais algumas solicitações de pull antes de estarem prontas para serem utilizadas.

Desculpe se este não é o lugar certo para fazer perguntas, mas não consegui encontrar nenhum lugar melhor. Apenas 2 perguntas:

  1. Uma única função pode ser condicionalmente const? Por exemplo, uma função pode ter 2 assinaturas como:

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

    Neste caso, foo é const iff A: const T ; se A não implementar const T , ele ainda satisfaz o limite, mas foo não é mais const. Também é importante para o autor ser capaz de especificar qualquer limite genérico para exemplos mais complexos (ex. where A::Output : Bar ). Um ótimo exemplo disso é até mesmo a aritmética simples:

    // 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?
    

    Acredito fortemente que definitivamente deveria haver uma maneira de fazer isso, e estou surpreso que ela não seja mencionada na RFC (a menos que eu tenha esquecido?).

  2. _ [menos importante]: _ Haverá uma maneira de detectar no corpo de uma função const se estamos executando em tempo de compilação ou em tempo de execução? Acho que alguma macro semelhante a cfg!() seria uma extensão útil, nem que seja por outro motivo que a depuração. cfg!() já está avaliado em tempo de compilação, então eu acho (/ acho) que deve ser capaz de saber se a função está sendo usada como const ou regular, uma vez que isso também é determinado na compilação -Tempo. No entanto, isso é menos importante do que minha primeira pergunta.

@ Coder-256

  1. Sim, consulte https://github.com/rust-lang/rfcs/pull/2632.
  2. Não tenho certeza se isso deveria ser possível, embora, considerando o que precede, também não tenha certeza de que seja necessário.

@ Coder-256 Ambas as questões não estão relacionadas a genéricos const, mas sim a funções const. Os genéricos Const são para serem genéricos sobre consts (por exemplo, foo<2>() ) ao invés de funções que podem ser executadas em tempo de compilação. Imagino que seja por isso que você não encontrou as respostas para suas perguntas no RFC 2000.

@rpjohnst Obrigado, mas acho que não fui claro. Já vi rust-lang / rfcs # 2632 e rust-lang / rfcs # 2000, mas tenho certeza de que isso não é mencionado em nenhum dos dois. (mas posso estar errado?) O que estou perguntando é sobre funções const condicionalmente . Veja os exemplos que escrevi, pois são difíceis de descrever.

@yodaldevoid Opa, você está certo, onde devo perguntar isso?

Quanto à questão macro, concordo que não há realmente muito uso para ela agora que penso a respeito

O que estou perguntando são funções condicionalmente const. Veja os exemplos que escrevi, pois são difíceis de descrever.

Sua definição square_const pode ser usada no lugar de square (isto é: ela é forçada a uma função com a assinatura equivalente em tempo de execução). Veja https://github.com/rust-lang/rfcs/pull/2632 para uma discussão sobre este comportamento (também, esse tópico é o lugar certo para fazer perguntas sobre quaisquer interações entre const fn e limites de características).

@varkor Não estou convencido de que seja esse o caso (já que os limites do trait mudam), mas vou perguntar em rust-lang / rfcs # 2632.

Relatório de falha:

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 : const generics ainda não foi totalmente implementado e não se espera que funcione. Faremos um anúncio quando chegar a hora de começar a experimentar #![feature(const_generics)] (sobre o qual você será notificado se estiver inscrito nesta edição).

@varkor Espere , o exemplo de _marker: PhantomData<(LOWER, UPPER)> - esses são dois const parâmetros usados ​​como tipos, por que rustc_resolve produziu um erro?

Bom ponto: vou investigar isso. Observe que este é apenas um problema com #![feature(const_generics)] , portanto, não é um problema crítico (o uso de consts não genéricos produz o erro conforme o esperado). Talvez nunca chegue a rustc_resolve .

~ @eddyb : este ICE está vindo de resolve_ident_in_lexical_scope , então imagino que provavelmente esteja relacionado a https://github.com/rust-lang/rust/issues/58307.~

Editar: na verdade, talvez não - isso parece se aplicar apenas a macro_rules! .

Minimizado:

#![feature(const_generics)]

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

Resolva os ICEs antes de produzir o erro "tipo esperado, valor encontrado".

O índice está fora dos limites aqui:
https://github.com/rust-lang/rust/blob/master/src/librustc_resolve/lib.rs#L3919

O atual noturno produz "parâmetro N nunca é usado" para o seguinte código:

struct Foo<const N: usize> { }

Da discussão anterior, achei que o compile deveria aceitar esse código. É simplesmente um artefato da implementação inacabada?

Parâmetros de tipo normal PhantomData<n> ?

Acho que já é possível fazer PhantomData<[(); N]> . Não tenho certeza se isso é algo que realmente queremos impor, porém, como AFAIU o objetivo de PhantomData é marcar a variação, e não há AFAIU nenhuma noção de variância wrt. um parâmetro genérico const.

E isso só funciona quando N é do tipo usize .

Decidimos que não era necessário usar parâmetros const durante a discussão RFC e a implementação atual é um bug.

@withoutboats Você pode indicar onde no RFC isso é indicado? Tentei encontrar algo nesse sentido e devo ter perdido.

não sei se foi incluído no texto RFC

@semoutboats Você se importaria de apontar onde isso foi discutido? Eu pesquisei no RFC PR, mas não me surpreendeu.

@yodaldevoid Este comentário foi citado anteriormente: https://github.com/rust-lang/rust/issues/44580#issuecomment -419576947. Mas não estava no RFC PR, e sim neste assunto.

Não posso vasculhar o histórico de comentários de vários anos atrás, mas posso explicar: usar variáveis ​​de tipo é necessário como um bloqueador para fazer você pensar sobre a variância desses parâmetros (IMO, isso também é desnecessário e podemos usar como padrão covariante, mas isso é um problema separado). Os parâmetros Const não têm nenhuma interação com a variância, portanto, isso não teria motivação.

@ HadrienG2 Obrigado por encontrar um comentário relevante.

@semoutboats Eu realmente não esperava que você fosse arrastar por todos esses anos de comentários para isso, era apenas uma esperança que você já tivesse em mãos.

Obrigado pela explicação. Devo admitir que nunca consigo envolver minha mente em torno da variância, não importa quantas vezes eu tente aprendê-la, mas mesmo sem isso, quando pensado novamente, não faz sentido o uso de parâmetros de custo. Vou colocar a correção desse bug em nossa lista de FIXMEs.

Aqui está um resumo do progresso até agora nos genéricos Const.


Antes que o trabalho com os genéricos const pudesse começar corretamente, havia alguma refatoração que precisava ser feita. @jplatte assumiu a tarefa com https://github.com/rust-lang/rust/pull/45930. @jplatte então começou a trabalhar na implementação principal dos genéricos const, mas descobriu que não tinha tempo suficiente para continuar.

@yodaldevoid e eu continuamos de onde @jplatte parou, mas rapidamente descobrimos que o progresso era dificultado pela maneira como os parâmetros genéricos eram tratados em geral. Assim, começou uma série de mudanças para renovar a maneira como os genéricos eram tratados em toda a base de código: 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.

Feito isso, a implementação dos genéricos const poderia começar para valer. Desde então, @yodaldevoid e eu, lenta mas seguramente, adicionamos suporte 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 e mais recentemente https://github.com/rust-lang/rust/pull/59008. (Eles foram divididos principalmente da solicitação principal const generics pull .)


Qual é o status agora? Alguns testes de genéricos const agora funcionam 🎉 No entanto, ainda existem aqueles que não funcionam, e há uma série de FIXME s em todo o código que ainda temos que resolver. Estamos chegando perto agora, porém, e estamos em um estágio em que há algumas frutas ao alcance da mão, se você quiser ajudar.

  • Primeiro, precisamos de mais testes. Existem apenas alguns testes de genéricos Const até agora, mas gostaríamos de muitos mais. No momento, como estamos cientes de uma série de problemas, não precisamos de relatórios de bug, mas precisamos passar nos testes. Se você criar um caso de teste que funcione (e não seja muito semelhante a um teste existente), sinta-se à vontade para abrir uma solicitação pull para adicioná-lo.
  • Em segundo lugar, como mencionado, há uma série de FIXME(const_generics) espalhados por todo o código. Estamos planejando trabalhar nosso caminho através deles, mas @yodaldevoid e eu não temos muito tempo, então se você acha que pode resolver um, vá em frente (embora você possa querer deixar um comentário dizendo isso, então nós não fazemos t esforços duplicados).

Eu escrevi uma visão geral de alguns dos problemas de implementação restantes antes que const generics esteja pronto para o teste adequado, na postagem superior, para dar uma ideia aproximada do que resta fazer.

Demorou, mas temos feito progressos constantes e esperamos manter o ritmo. É motivador finalmente ver as coisas começando a se encaixar!

Estou esperando meses para este post @varkor :) Não sou nenhum mago do Rust, mas gostaria de enfrentar um dos FIXME s

Parabéns @jplatte , @yodaldevoid e @varkor. Este é um grande passo para se livrar das características personalizadas de Array-like nas bibliotecas matemáticas.

Postagem cruzada...

@varkor Em relação aos testes, talvez ajude saber o que se espera que funcione. Por exemplo, fiquei surpreso que isso não funcionasse: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=d84ffd15226fcffe02c102edb8ae5cf1

Além disso, para referência dos interessados ​​em FIXMEs: https://oli-obk.github.io/fixmeh/

@marque-im tente colocar {} torno de FOO . Assim vai funcionar.

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

Ao aplicar uma expressão como parâmetro const (exceto para matrizes), que não é uma expressão de identidade, a expressão deve estar contida em um bloco. Essa restrição sintática é necessária para evitar a exigência de infinito lookahead ao analisar uma expressão dentro de um tipo.

{expression} só deve ser necessário se a expressão não for um identificador ou literal, ou seja, um bug.

Citando o mesmo RFC:

Expressão de identidade: uma expressão que não pode ser avaliada posteriormente, exceto substituindo-a por nomes no escopo. Isso inclui todos os literais, bem como todos os idents - por exemplo, 3, "Olá, mundo", foo_bar.

@ mark-im Sim, atualmente não podemos dizer a diferença entre idents para tipos e consts na análise inicial. Nós tomamos a decisão de como lidar com tudo isso no futuro. Suponho que agora pode ser o futuro.

Para um pequeno histórico, falamos sobre duas maneiras que me lembro de como lidar com isso. A primeira maneira é forçar as pessoas a marcarem argumentos const (digitando const antes deles ou de alguma outra forma). Isso não é ótimo do ponto de vista ergonômico, mas é fácil do ponto de vista da análise. A segunda maneira é tratar todos os argumentos genéricos como iguais até começarmos a emparelhá-los com os parâmetros genéricos mais tarde, durante a compilação. Este é provavelmente o método que queremos seguir, e já houve algum trabalho para tornar isso possível; apenas não demos o salto final.

Ótimo trabalho. Fico feliz em ajudar, consertando alguns dos FIXME , se possível. Não tenho muita experiência com a base de código rustc, então vou começar no FIXME https://github.com/rust-lang/rust/blob/master/src/librustc_mir/monomorphize/item.rs# L397 , pois parece que seria fácil.

E o seguinte código:

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

Atualmente resulta em "nenhum item associado denominado N encontrado para o tipo Self no escopo atual" erro de compilação. Esse código será aceito após o término de FIXME s ou exigirá esforço adicional para implementação?

@yodaldevoid

Pergunta rápida, desculpas se isso já foi discutido.

A segunda maneira é tratar todos os argumentos genéricos como iguais até começarmos a emparelhá-los com os parâmetros genéricos mais tarde, durante a compilação. Este é provavelmente o método que queremos seguir, e já houve algum trabalho para tornar isso possível; apenas não demos o salto final.

Isso não vai contra a corrente segundo o princípio que Rust assume ao tornar as assinaturas de função explícitas para evitar a produção de erros relacionados à implementação de uma função? Talvez eu esteja totalmente mal-entendido e você esteja falando sobre a análise de parâmetros genéricos dentro do corpo de uma função.

Isso não vai contra a corrente segundo o princípio que Rust assume ao tornar as assinaturas de função explícitas para evitar a produção de erros relacionados à implementação de uma função? No entanto, talvez eu esteja totalmente mal-entendido e você esteja falando sobre a análise de parâmetros genéricos dentro do corpo de uma função.

Trata-se de determinar se os identificadores passados ​​como parâmetros genéricos para uma função são constantes ou tipos.

Exemplo:

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

Note que ao definir a função, você deve especificar que NAME é uma constante, mas ao chamá-la, não é necessário dizer que HIS_NAME é uma constante dentro do operador turbo peixe ( ::< > ).

@zesterer Se esta solução proposta fosse transparente para o usuário (por exemplo, ao definir uma função, não havia diferença entre os tipos de parâmetros), você estaria correto. No entanto, a ideia por trás da solução proposta não é mudar o comportamento do usuário, mas apenas mudar a implementação. O usuário ainda escreveria assinaturas de função com parâmetros explícitos de tipo, const e tempo de vida, e os argumentos genéricos ainda precisariam corresponder a um parâmetro correspondente, exatamente como o analisamos no compilador mudaria.

@newpavlov Estou surpreso que o código não funcione. Agradecemos se você pudesse enviar uma edição separada.

@yodaldevoid @robarnold

Ah, peguei você. Eu presumi erradamente que isso estava relacionado a assinaturas de tipo / função.

Para usar genéricos const em tipos de array integrados, abri # 60466 para ver as opiniões de outras pessoas sobre isso.

Os blocos Impl parecem completamente quebrados no momento. Isso é esperado no estado atual do recurso ou devo abrir outro problema sobre isso?

Os blocos Impl parecem completamente quebrados no momento. Isso é esperado no estado atual do recurso ou devo abrir outro problema sobre isso?

Você precisa adicionar {} s em torno de N , impl<const N: usize> Dummy<{N}> {} .

Mas sim, você obterá um erro sobre o parâmetro irrestrito.

Ah, obrigado. Esqueci a coisa do aparelho!

Não estou surpreso que ele falhe uma vez que seja resolvido, uma vez que este foi um caso de teste altamente minimizado.

... mas sim, este provavelmente deve funcionar:

#![feature(const_generics)]

trait Dummy {}

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

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

... e ainda falha com 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.

... e suponho que seja isso o que @varkor quis dizer com "problemas com manipulação de array para genéricos consts" em # 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
Consulte # 60619 e # 60632.

Eu acho que este código deve construir:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=e45b7b5e881732ad80b7015fc2d3795c

mas atualmente erros:

erro [E0119]: implementações conflitantes do traço std::convert::TryFrom<[type error]> para o tipo MyArray<_, _> :

Infelizmente, essa é uma restrição de Try{From,Into} e não tem nada a ver com genéricos const. Eles não podem ser implementados genericamente em muitos casos: cercadinho

@oberien em seu exemplo, T é um tipo estrangeiro e não se sabe se T: Into<Foo<T>> é válido ou não.

No meu exemplo, [T; N] é um tipo definido em libcore, e é #[fundamental] (tbh eu nem sei o que isso significa), então eu acho que o resolvedor de características realmente sabe que [T; N]: Into<MyArray<T, {N}>> não é válido, e aí não deve ser um conflito, eu acho?

[...] e é #[fundamental] [...]

Este é o problema, fundamental está relacionado a permitir que os usuários downstream definam implementações de características quando envolvem um tipo local no tipo de wrapper fundamental. Você pode ver neste playground que sua implementação funciona entre tipos não fundamentais, mas falha se o tipo from estiver marcado fundamental (com uma mensagem de erro muito melhor).

@varkor @yodaldevoid Então, acabei em uma situação em que S<{N == 0}> (com S tomando um parâmetro const bool e N a const usize ) não é nem S<{true}> ou S<{false}> no olho do compilador (no sentido de que não implementa as mesmas características que ambos), e não tenho certeza se isso é um bug ou um limitação esperada do protótipo atual. Você pode me dar uma rápida atualização sobre a lógica de unificação atual?

Como um exemplo minimizado, este ...

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

... produz o seguinte erro:

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

Na verdade, não sei, mas acho que o problema pode ser que o compilador não tem a lógica para decidir se o 0 literal é u8 ou u16 . Experimente algo como: 0u8 as _

Não é isso, a mensagem de erro continua a mesma.

Mas se você quiser uma versão que não envolva inferência de tipo inteiro, aqui está uma minimização mais boba:

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
}

Ainda falha devido a Test<{B}> supostamente não implementar Not .

Oi ! Não tenho certeza se a unificação em valores const deve funcionar ou não, mas há um novo sistema de unificação em andamento ("giz") que está sendo trabalhado, mas não é o que o rustc usa atualmente.
Então o que você está tentando fazer _pode_ ter que esperar até que o giz esteja pronto. E mesmo assim, não tenho certeza se funcionará ou deveria funcionar até mesmo com giz.
Consulte https://github.com/rust-lang/rust/issues/48049 para ver o progresso.

@ HadrienG2 você poderia registrar uma questão separada para isso? Não estou totalmente surpreso que isso não funcione no momento devido a alguns outros problemas que estamos investigando, mas este é um novo caso e gostaria de que fosse rastreado.

@carado A unificação é feita em consts ou nada disso funcionaria. Os genéricos Const não são bloqueados em giz.

@ HadrienG2 assim que tivermos a especialização para genéricos const, você poderá fazer 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
}

O que você quer fazer é algum tipo de verificação de exaustividade, como se fosse uma correspondência estendida apenas para o sistema de características. Não sei se isso está sendo mencionado em qualquer tópico.

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

@carado Chalk não diz respeito aos "primitivos do sistema de tipos" reais, dos quais este é um deles.
Ou seja, rustc ainda precisa (mesmo hoje, uma vez que Chalk já está parcialmente integrado) implementar a parte da unificação que lida com entidades opacas para Chalk (como duas expressões constantes diferentes).
Se implementarmos isso (o que não temos planos para um futuro próximo, de qualquer maneira), não mudará muito, mesmo depois que Chalk substituir o sistema de características (que é seu objetivo principal, não a unificação).

Oh, que pena então! Acho que realmente não sei do que estou falando.

Uma das coisas incríveis que const generics forneceria são funções simples computadas em tempo de compilação, por exemplo fatorial.

Exemplo:

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

Tanto quanto eu entendo, há algum suporte limitado para genéricos const noturnos? Em caso afirmativo, existe algum lugar mais organizado do que este assunto onde posso encontrar como usá-lo e outras coisas?

@ dancojocaru2000 const funções devem ser a forma preferida para cálculo de nível de valor em tempo de compilação, por exemplo, https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=4994b7ca9cda0bfc44f5359443431378 , o que acontece não funcionem porque ainda não estão totalmente implementados. Mas também não são const genéricos.

Seu snippet pode ser problemático porque não tenho certeza se match deve funcionar para const argumentos. Você também misturou adição com multiplicação no código, mas isso não importa muito.

Acho que você já pode fazer fatoriais em tempo de compilação com uma codificação Peano, que geralmente é mostrada como uma demonstração para outras linguagens funcionais.

funções simples computadas em tempo de compilação

Acho que o recurso mais apropriado seria const fn .

Teoricamente, seu código funcionaria quase 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>());
}

Mas não:

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`

Embora você realmente deva usar um tipo sem sinal:

#![feature(const_generics)]

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

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

tem algum lugar mais organizado do que este assunto onde eu possa encontrar como usar e outras coisas?

Isso geralmente é coberto pelo livro instável , mas não há nada útil nele agora. À medida que você descobre bits, talvez possa considerar começar a esboçar algum conteúdo para isso.

fatoriais em tempo de compilação com uma codificação Peano,

Para um exemplo disso, veja a caixa do tipo typenum

Teoricamente, seu código funcionaria quase como está

Ao chamar factorial::<0> o _ => factorial::<{X - 1}>() * X também seria codificado, certo? Mas tentar fazer isso causaria um estouro negativo de inteiro.

Posso esperar que um código como este seja compilado no 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) { }

Isso é cruzado com uma implementação recente de alguns arrays [T; N] para N <= 32 como genéricos constantes, mas o código a seguir não compila em um ( 4bb6b4a5e 2019-07-11 ) mais recente todas as noites com vários erros 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;

embora o seguinte faça:

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

Parece que N*2 não é um const ou qualificador válido para tal restrição

@shamatar N*2 deve estar entre colchetes como `[u64; {N * 2}]

Acho que os erros de "característica não implementada" vêm das macros derivadas que não adicionam o limite para [u64; {N*2}] , mas se as derivações forem removidas, há um ICE atualmente:

Erro `` `: erro interno do compilador: constante no tipo teve um erro ignorado: TooGeneric
-> src / main.rs: 5: 1
|
5 | / pub struct BigintRepresentation <
6 | const N: usize
7 | > (pub [u64; N])
8 | onde [u64; N]: std :: array :: LengthAtMost32,
9 | [u64; {N * 2}]: std :: array :: LengthAtMost32;
| | ____________________________________________ ^

thread 'rustc' entrou em pânico com 'nenhum erro encontrado, embora delay_span_bug emitido', src / librustc_errors / lib.rs: 366: 17
nota: execute com a variável de ambiente RUST_BACKTRACE=1 para exibir um backtrace.

Este código não 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 você precisa fazer Foo<{X}>

@ pengowen123 Sim, ele compila (trava o compilador com constant in type had an ignored error: TooGeneric ) sem derive , mas então pode ser uma questão separada se essa "restrição dupla" que é efetivamente N <= 32 e N <= 16 não é aplicado pelo compilador.

Expressões que mencionam parâmetros provavelmente não funcionarão por mais algum tempo, [T; N] e Foo<{N}> têm letras especiais para não conter expressões com uma menção de N , mas sim consulte diretamente o parâmetro N , ignorando o problema maior.

Usar Self causa um travamento.
Mesmo trocando-o por Value<{C}> , ele ainda trava.

#![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();
}

Da mesma forma que # 61338, a fonte do problema é a compilação incremental.

funções simples computadas em tempo de compilação

Acho que o recurso mais apropriado seria const fn .

Teoricamente, seu código funcionaria quase 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>());
}

Mas não:

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`

Embora você realmente deva usar um tipo sem sinal:

#![feature(const_generics)]

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

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

Este recurso é útil para mim também, const fn não é suficiente. Eu quero usá-lo para uma matriz N-dimensional com uma condição de terminação de dimensões = 0.

Sou capaz de criar uma estrutura:

#![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]
        }
    }
}

Isso falha porque não consigo inicializar a matriz devido a:

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

Tentei contornar isso definindo os valores const definidos para o valor genérico.

Isso supera o erro acima, mas o compilador trava.

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=[]

A linguagem precisa desesperadamente de genéricos constantes básicos, de forma que a biblioteca padrão não precise definir cada função para cada tamanho de array. Esta é a única razão pela qual não uso ferrugem. Nós realmente precisamos de funções completas de tempo de compilação?
Posso estar errado, mas expressões inteiras simples devem bastar e espero que ninguém esteja perdendo tempo, certificando-se de que esses exemplos malucos funcionem.

A linguagem precisa desesperadamente de genéricos constantes básicos, de modo que a biblioteca padrão não precise definir cada função para cada tamanho de array

Já existe algum esforço lá https://github.com/rust-lang/rust/issues/61415.

Espero que ninguém esteja perdendo tempo, garantindo que esses exemplos malucos funcionem.

Algumas pessoas fazem, não vejo nenhum problema nisso;)

Esta é a única razão pela qual não uso ferrugem.

Este é o motivo menos interessante que vi citado para alguém que não usa ferrugem. Tem certeza de que está falando a verdade?

mas expressões inteiras simples devem ser suficientes

Fazer até mesmo esse escopo reduzido é incrivelmente difícil. Experimente, temos pessoas realmente capazes fazendo isso e há um motivo pelo qual está demorando tanto.

Infelizmente, não tenho conseguido dedicar muito tempo para consertar bugs const genéricos recentemente (ou para Rust de forma mais geral). Se alguém quiser se envolver na promoção de genéricos const, eu ficaria feliz em oferecer conselhos sobre como lidar com os problemas em aberto e revisar as correções de bugs, embora provavelmente demore um pouco antes que eu possa me concentrar nisso novamente.

Espero que ninguém esteja perdendo tempo, garantindo que esses exemplos malucos funcionem.

Ninguém é, miri já é muito mais poderoso do que C ++ constexpr .
E ninguém está trabalhando em nada sofisticado como derivar N = M de N + 1 = M + 1 .

A maioria desses bugs não são sobre as expressões neles, eles são sobre o sistema de tipos e como const genéricos interagem com todos os outros recursos.
Eles ainda estariam lá se você tivesse apenas const genéricos e literais inteiros.

A propósito, acho que o erro "comprimentos de array não podem depender de parâmetros genéricos" para [expr; N] expressões não são necessárias, poderíamos usar o mesmo truque que fazemos para [T; N] tipos e puxar fora o N sem avaliá-lo como uma expressão.

Embora eu queira tentar fazer isso, não tenho certeza se sou a pessoa certa. Usei pouca ferrugem e conheço muito pouca teoria do compilador. Posso precisar de um pouco de treinamento, mas certamente estou disposto. 😄

Edit: Tenho um pouco de experiência em software em geral.

@varkor , estou procurando algo útil para fazer em rustc e adoraria ajudar e ajudar. Além disso, obrigado por ser franco sobre sua capacidade de alocar tempo para isso!

@varkor , na verdade estou seguindo o comentário acima, então, se você tiver algum conselho, sou todo ouvidos! Sinta-se à vontade para me indicar outro canal de comunicação também.

Uma coisa que poderíamos fazer agora, acabei de perceber é permitir que Foo::<{...}> / [expr; ...] expressões se refiram a parâmetros genéricos (na parte ... ).

Isso ocorre porque as expressões devem ser aninhadas em algum lugar dentro de um corpo e isso tende a evitar dependências cíclicas.

No entanto, estou preocupado que ter, por exemplo, [(); [0; 1][0]] em uma assinatura iria quebrar, então provavelmente precisaríamos de uma cratera para correr de qualquer maneira (e hoje em dia isso leva uma eternidade).

Para qualquer pessoa interessada em ajudar com genéricos Const, meu conselho seria dar uma olhada na lista de questões abertas de genéricos Const e investigar o que parecer interessante para você. Alguns já fizeram alguma investigação, que deve estar nos comentários. A maioria dos problemas provavelmente exigirá um pouco de investigação. É útil fazer um comentário se você estiver planejando investigar algo, por isso não duplicamos os esforços (mas muitas vezes você pode ignorar o responsável pelo problema se não houver nenhuma atividade por um tempo). Se você tiver alguma dúvida, pode perguntar nos comentários do problema, ou no Discord ou no Zulip. @eddyb , @yodaldevoid , @oli-obk e eu estamos familiarizados com muitas das áreas relevantes e somos boas pessoas para perguntar. Obrigado por todo o seu interesse!

cc @hameerabbasi , @ranweiler

Perguntas sobre genéricos const:

  1. Onde posso encontrar a documentação (para não ter que fazer perguntas aqui)
  2. Que limitações existem neste momento ao usar um parâmetro const de tipo arbitrário?
  3. Quais recursos importantes de const genéricos não foram implementados / travaram o compilador?

PS (Colaboradores :) Muito obrigado por trabalhar neste recurso.

const-genéricos ainda está em desenvolvimento, então não há nenhum documento oficial sobre ele ainda. Não tenho muita certeza sobre as outras duas perguntas. Quando usei const-genéricos pela última vez, eles travaram quando especifiquei alguns parâmetros const-genéricos, mas já se passou quase um mês desde que fiz algo com ele pela última vez, então as coisas podem ter mudado desde então.

E ninguém está trabalhando em nada sofisticado como derivar N = M de N + 1 = M + 1 .

Isso seria muito útil ter um solucionador para tais restrições de tipo. Por exemplo, ao implementar uma adição de dois N bits, o tamanho de retorno deve ser (N + 1) para contabilizar um bit de estouro. Quando (por exemplo) dois números de 5 bits são fornecidos, o solucionador deve verificar se N + 1 = 6 . Esperançosamente, isso pode ser anexado aos genéricos Const mais tarde :)

@ flip111 Sim, acho que o plano é adicionar isso mais tarde, mas esse tipo de restrição de expressão geral é muito complexo e difícil de implementar. Portanto, podemos não vê-los por pelo menos alguns anos.

Para ser honesto, resolver esses tipos de problemas baseados em restrições soa muito como um trabalho para a Prolog. Talvez pegar carona no motor de giz seja uma opção? Afaik ele resolve Traços, certo?

A propósito, eu amo o tópico, embora não possa me dar ao luxo de ajudar com este caixa eletrônico. Agradeço a todos que estão trabalhando em Const Generics por seu tempo e dedicação. 💐

Uma pequena atualização: estou subindo na árvore Rust. Eu pretendo contribuir, mas quero estudar por conta própria para um lugar onde não precise de treinamento excessivo.

Sou capaz de criar uma estrutura:

#![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]
        }
    }
}

Isso falha porque não consigo inicializar a matriz devido a:

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

Encontrei o mesmo problema, mas encontrei uma solução alternativa usando MaybeUninit :
https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=3100d5f7a4efd844954a6fa5e8b8c526
Obviamente, é apenas uma solução alternativa para obter matrizes inicializadas corretamente, mas para mim isso é suficiente até que uma maneira adequada seja disponibilizada.
Observação: acho que o código deve sempre funcionar conforme o planejado, mas se alguém encontrar um bug com o uso do inseguro, ficarei feliz em corrigi-lo.

@raidwas, você está perdendo memória não inicializada ao usar o operador = para inicializar a memória não inicializada. Faça isso em vez disso,

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

@KrishnaSannasi obrigado, boa pegada: D (tecnicamente, eu não eliminei a memória não inicializada, pois só a uso para primitivos, mas é bom ter uma versão correta aqui)

Tecnicamente, descartar até mesmo floats é indefinido, mas não é explorável agora.

Tecnicamente, descartar até mesmo floats é indefinido, mas não é explorável agora.

Eu esperava que descartar qualquer tipo Copy , mesmo os não inicializados, fosse um autônomo. Talvez valha a pena mudar.

Tecnicamente, descartar até mesmo floats é indefinido, mas não é explorável agora.

Eu esperava que descartar qualquer tipo Copy , mesmo os não inicializados, fosse um autônomo. Talvez valha a pena mudar.

Ainda é tecnicamente UB. Embora descartar os valores Copy definidos com segurança seja autônomo, o compilador pode decidir fazer algumas otimizações inesperadas se você tentar eliminar a memória não inicializada de qualquer tipo (por exemplo, remover todos os códigos que possivelmente tocarem esse valor), que poderia quebrar coisas. Esse é o meu entendimento sobre isso.

Não quero ser indelicado, mas ≥ 60 pessoas são notificadas sobre comentários neste tópico, então provavelmente devemos manter a discussão fora do tópico ao mínimo. Em vez disso, vamos usar isso para coordenar o trabalho com genéricos const, pois é isso que todos nós queremos que aconteça.

Falha se eu usasse _aliases apontando para o tipo com const-param_ de outra caixa (dependência).

Por exemplo:

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

    ! [recurso (const_generics)]

Alias ​​de tipo de pub= Struct;
Pub struct Struct(T);

- crate B

extern crate crate_a;
use crate_a :: Alias;

pub fn inner_fn (v: Alias) {}
`` `
crash-log (44580) .txt

@ fzzr- soa como # 64730

Eu tenho trocado alguns códigos para genéricos const e parece que existem realmente dois casos de uso diferentes. Não tenho certeza se eles devem ser combinados ou se seria melhor usarmos duas sintaxes diferentes para os casos de uso.

Muitos dos meus usos não são realmente para tipos onde um valor constante desempenha um papel na determinação dos tipos, mas sim para me valer de recursos que estão bloqueados para valores não constantes / literais (ainda não totalmente suportados, por exemplo, padrões de correspondência, mas, no final das contas, precisará ser).

IMHO devemos formalmente colocar "argumentos const" junto com genéricos const, para que as pessoas não escrevam código mutante introduzindo mil "genéricos const" (um para cada argumento) para fazer o compilador avaliar certas variáveis ​​como literais / constantes.

@mqudsi Você poderia dar um exemplo? Já existem planos e um trabalho fundamental em andamento para tornar a const eval mais poderosa. No entanto, isso é ortogonal aos genéricos const.

O que quero dizer é, se você deseja refatorar o código analisador típico como o seguinte para reutilização:

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

Você normalmente o moveria para uma função, exceto que o seguinte não funcionará porque certos valores que são usados ​​na correspondência de padrões tornaram-se variáveis:

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

    ...
}

Se const generics chegar antes dos argumentos const, posso abusar repentinamente de const generics para chegar ao seguinte:

#[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 hoje, mesmo esse código não funcionará porque o compilador não detecta os valores genéricos const como constantes válidas para uso em um padrão, mas isso é obviamente incorreto e precisa ser corrigido antes que o RFC 2000 possa chegar. Não me interpretem mal, tenho lutado por constantes genéricas há anos e tenho PRs prontos para uma dúzia de caixas principais (mal posso esperar para definir o fuso horário em chrono a const genérico e unificar todos os vários tipos de DateTime ), mas imho se for possível abusar de genéricos const para falsificar argumentos const (onde por "const" o que realmente queremos dizer é "literal"), então você verá abuso disso.

Não é necessariamente o fim do mundo, mas sem ter nos aprofundado muito nele, parece que uma implementação adequada e completa dos genéricos const incluirá necessariamente todo o encanamento para os argumentos const de qualquer maneira, então podemos aproveitar o tempo extra para finalizar a sintaxe / ux / story para argumentos const enquanto estamos nisso e evitar uma era infeliz de fedor de código. Sim, o acima ainda pode ser feito com macros, mas a ergonomia dos genéricos const é mil vezes mais fácil.

fwiw, é assim que eu imagino que a versão do argumento const se pareça com:

#[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(())
        }
    }

    ...
}

Seria virtualmente idêntico ao const genéricos, exceto pela semântica.

Você normalmente o moveria para uma função, exceto que o seguinte não funcionará porque certos valores que são usados ​​na correspondência de padrões tornaram-se variáveis:

Este parece ser um caso de uso que é mais prontamente endereçado, permitindo que os valores das variáveis ​​associadas sejam usados ​​em padrões (não apenas consts), com alguma nova sintaxe. O fato de que consts são permitidos atualmente em padrões é contra-intuitivo e, tanto quanto eu sei, histórico.

@mqudsi Perdoe-me se esta é uma pergunta boba, mas não vejo nada de errado no exemplo que você deu. Parece um caso de uso perfeitamente válido para genéricos const para mim: ter uma definição que é generalizada para trabalhar com valores arbitrários como max / min. Eu realmente não vejo os benefícios dos argumentos const sobre os genéricos Const. Eles parecem equivalentes para mim; isto é, os argumentos const podem ser implementados apenas como uma solução para os genéricos const. Você poderia explicar melhor o que há de errado com esse padrão de design?

Aqui está um resumo do trabalho com genéricos const desde a última atualização . Da última vez, era tudo sobre a implementação central: certificar-se de que tudo se encaixava e fazer com que alguns dos casos de teste básicos passassem. Desde então, o esforço tem se concentrado em tornar const genéricos mais confiáveis: consertando casos que travaram o compilador ou não funcionaram inesperadamente, ou aprimorando os diagnósticos. Estamos nos aproximando de algo que funciona de maneira confiável, embora ainda haja um caminho a percorrer.


  • @ skinny121 tem feito um trabalho fantástico no último mês, corrigindo vários problemas pendentes com genéricos const:

    • Expandindo o pacote de testes (https://github.com/rust-lang/rust/pull/60550)

    • Correção de problemas de inferência de tipo (https://github.com/rust-lang/rust/pull/64679, https://github.com/rust-lang/rust/pull/65579)

    • Compatível com mais tipos de genéricos const, incluindo strings e fatias (https://github.com/rust-lang/rust/pull/64858) e ponteiros (https://github.com/rust-lang/rust/pull/64986 ) (embora observe que eles provavelmente não serão estabilizados imediatamente ao lado do restante dos genéricos const, pois não são compatíveis com o RFC original )

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

    • Correção de vários problemas com o uso de cross-crate de genéricos const (https://github.com/rust-lang/rust/pull/65365)

  • @eddyb corrigiu alguns problemas com a avaliação const afetando genéricos const (https://github.com/rust-lang/rust/pull/63497)
  • @matthewjasper corrigiu alguns problemas com o uso de genéricos const em macros (https://github.com/rust-lang/rust/pull/63083)
  • @davidtwco corrigiu um problema envolvendo genéricos
  • @GuillaumeGomez corrigiu a exibição de genéricos const no Rustdoc (https://github.com/rust-lang/rust/pull/61605)
  • @varkor corrigiu alguns problemas de inferência de tipo (https://github.com/rust-lang/rust/pull/61570, https://github.com/rust-lang/rust/pull/60742, https: // github. com / rust-lang / rust / pull / 60508), um problema que restringe os locais onde os genéricos podem ser usados ​​(https://github.com/rust-lang/rust/pull/60717) e várias falhas do 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 )

Além disso, outro trabalho no compilador acabou corrigindo alguns dos outros problemas relacionados aos genéricos const.

Também começamos a usar const generics dentro do próprio compilador: implementações de array trait agora usam const generics graças ao trabalho de @ crlf0710 e @scottmcm (https://github.com/rust-lang/rust/pull/60466, https : //github.com/rust-lang/rust/pull/62435), que levou a melhorias de desempenho e que também nos permitirá não restringir implementações de traços de array no futuro (quando os genéricos const forem estabilizados). @Centril usou a mesma abordagem para melhorar VecDeque (https://github.com/rust-lang/rust/pull/63061).


Muitos dos bugs particularmente comuns com genéricos const foram corrigidos nos últimos meses. @nikomatsakis está investigando a normalização preguiçosa , que deve corrigir uma série de problemas restantes, enquanto @jplatte está tentando consertar a desambiguação dos parâmetros const dos parâmetros de tipo (então você não precisará mais digitar {X} para um argumento const).

Também quero agradecer a @eddyb por toda a orientação e revisão dos genéricos const durante o desenvolvimento, que foi inestimável.

Ainda há uma série de outros problemas a serem resolvidos e, como antes, poderíamos usar toda a ajuda que pudermos obter - se você estiver interessado em resolver qualquer um dos problemas restantes , sinta-se à vontade para me enviar um email sobre o problema ou Discord ou Zulip para dicas sobre como começar.

@mqudsi @ mark-im Seria apropriado estender a alternativa sintática atual para genéricos, impl Trait na posição de argumento, para genéricos constantes? Esta é a sintaxe @mqudsi sugerida para este caso de uso.

Pessoalmente, acho que isso melhoraria a legibilidade de tais casos de uso, mas vejo um problema: os genéricos normais são usados ​​para passar dados, então eles estão limitados a um argumento. Esse não é o caso dos genéricos const, então como você os passaria? Fingir que são argumentos?
No caso de impl Trait na posição do argumento, ele também não pode ser usado com a sintaxe turbo-peixe. Para const no argumento, seria o inverso. Isso pode ser confuso.

Não tenho certeza se os benefícios superam a "estranheza" aqui, mas gostaria de compartilhar minhas ideias de qualquer maneira.

Eu me lembrei vagamente de uma discussão sobre isso antes, e agora eu encontrei: https://internals.rust-lang.org/t/pre-rfc-const-function-arguments/6709

@ PvdBerg1998 que parece uma péssima ideia. Eu realmente não entendo muito bem que <…> é uma _sintaxe difícil_ de ler. Duas possibilidades aqui:

  1. Você pensa, como eu, que não é difícil de ler, mesmo com vários limites.
  2. Você acha que é difícil de ler: então, talvez a solução seria colocá-los em uma cláusula where ?

Agora compare com o pré-RFC vinculado acima. Seria introduzir uma _tag_ (no sentido do sistema de tipo) diretamente na posição dos argumentos da função, isto é, ligações de variáveis ​​para o corpo de uma função.

Em outros termos: é muito confuso e, como várias pessoas afirmaram para impl Trait na posição arg, não é necessário. Não cometa o mesmo erro duas vezes para adicionar um recurso que não vale a pena. Especialmente se você "fingir que são argumentos". A ferrugem estaria indo na direção errada aqui. A sintaxe deve ser mais leve, sim, mas não mais confusa. Por favor pare.

@phaazon
Eu, pessoalmente, concordo plenamente com a proposta vinculada de que permitir constantes na posição de argumento será um impulso ergonômico realmente bom. Não se trata apenas de assinaturas de função (discutivelmente where cláusulas não "resolvem" o problema, mas tornará mais difícil entender as assinaturas, uma vez que você tem que analisar 3 lugares em vez de apenas um), mas também sobre como essas funções serão usadas. Comparar:

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

A segunda linha é muito mais natural na minha opinião do que a segunda. Outro exemplo seria a criação de matriz: MatrixF32::new(3, 3) vs. MatrixF32::new::<3, 3>() . Eu discordo veementemente que esta funcionalidade seja "muito confusa". Para o usuário, nada mais é do que um requisito adicional para um argumento de função. Já podemos emular argumentos const por meio de macros (veja os intrínsecos do SIMD dentro de std ), mas é muito feio e ineficiente.

Em relação a impl Trait na posição de argumento, inicialmente também fui contra esse recurso, mas depois de algum tempo mudei de ideia e acredito que foi uma boa decisão no final. A única parte incompleta no momento é a interação com parâmetros de tipo explícitos fornecidos por meio de peixes turbo. Acho que uma boa solução seria tornar impl Trait argumentos invisíveis para peixes turbo, ou seja, os usuários devem usá-los quando têm certeza de que a desambiguação de tipo explícita não será necessária. Isso nos permitiria reduzir significativamente a necessidade de _ dentro de peixes turbo em alguns cenários.

Eu acho que uma discussão de argumentos const está fora do assunto aqui, então provavelmente não deve continuar aqui.

Uma coisa que separa const argumentos de impl Trait argumentos (que @ PvdBerg1998 mencionou) é que eles são argumentos, com tudo o que isso implica para inferência de tipo.

Você não pode passar um tipo em si como um argumento, mas pode passar um valor, e deixar uma função inferir seus argumentos genéricos const a partir de seus argumentos normais entre parênteses é completamente razoável.

Isso é bastante comum em C ++, por exemplo:

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

Isso é sutilmente diferente de simplesmente aceitar um parâmetro const e transformá-lo em um parâmetro genérico const ( fn foo(const N: usize) -> fn foo<const N: usize>() ). Está mais perto de restringir um parâmetro com um parâmetro genérico const e, em seguida, inferir esse argumento genérico const a partir do argumento. Portanto, o exemplo de matriz acima pode ter a seguinte aparência:

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

... onde usize<X> é a sintaxe criada para "um usize com o valor X ." (Analogamente, você pode querer usize<X..Y> para tipos variados, que foram discutidos no contexto da generalização de NonZero .)

Se você quiser evitar laços em tipos de alcance, você pode olhar para as linguagens de tipo dependente que permitem que os parâmetros sejam usados ​​diretamente nos tipos, com um pouco de ajuste de escopo:

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

Eles não são argumentos. Eles são genéricos . Em um nível de sistema de tipo, isso significa que seus _valores_ vivem em tempo de compilação, enquanto os valores de um argumento de função vivem em tempo de execução. Misturar os dois é extremamente enganoso.

Como eu me preocupo com sistemas de tipos, esta proposta vai contra muitos princípios saudáveis ​​e sólidos. Você não quer misturar valores e tipos. Porque, sim, um N (note que eu não disse usize , eu disse N ), mesmo que você pense que é um valor, é muito mais parecido com um tipo do que um valor. Como justificativa, Haskell não tem genéricos const, mas tem DataKinds , permitindo levantar construtores de dados 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

Então:

fn foo<const X: usize>()

Aqui, X é mais parecido com um tipo do que com um valor.

Além disso, me preocupo com a monomorfização. Se eu ler isso:

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

Com sua proposta, se olharmos para a definição de foo , é difícil saber quais argumentos monorfizarão foo . Porque toda vez que você passa um valor diferente para um genérico const, você cria uma nova função completa.

Talvez pareça mais intuitivo por _você_ e _seus motivos_, mas também tenho motivos para dizer que não é nada intuitivo em termos de correção de tipo. Afirmar que são argumentos é como afirmar que funções parametrizadas, em matemática, têm seus argumentos de parâmetros. Você está confundindo parâmetros e argumentos, demonstrando o quão ruim é essa ideia.

Você pode ter me interpretado mal, eu disse especificamente " const argumentos ... são argumentos ," não " const genéricos." Meu primeiro exemplo de Rust também é exatamente equivalente a DataKinds ( usize<N> é a versão de nível de tipo de N ). Não há nenhum problema aqui em termos de correção de tipo - não estou discutindo em termos de intuição, mas por analogia aos sistemas de tipo existentes, estabelecidos e sólidos.

Quanto à sua preocupação com a monomorfização, não é diferente de hoje! Cada argumento pode potencialmente causar uma nova monomorfização. Eu também acrescentaria que a solução para esse problema é reduzir o custo de todos os genéricos compartilhando código não dependente entre instâncias, incluindo e especialmente convertendo genéricos const em valores de tempo de execução quando possível.

(E estou totalmente perplexo com sua afirmação de que estou confundindo parâmetros e argumentos, tive o cuidado de distingui-los.)

Eu entendi o que você quis dizer, mas ainda me sinto muito preocupado. Como o _argumento_ está marcado como _const_, isso significa que você não pode transmiti-lo a _um valor_, falando em tempo de execução. Você precisa passar um _valor constante_, que será mapeado para a _constante genérica_. A propósito, você não mencionou isso aqui, mas isso é apenas uma aplicação de um _tipo de singleton_ se você considerar 10 como um tipo: seu único valor possível é 10 .

Como tenho sido contra impl Trait na posição arg, sou contra esse também (e você entenderá por que podemos comparar aqui). Vários pontos:

  1. Não acho que seja necessário enquanto _const genéricos_ adicionar muitos valores.
  2. Uma vez que são semelhantes aos tipos, eles devem pertencer onde os tipos estão localizados, não onde as variáveis ​​estão localizadas.
  3. Eu realmente gostaria de saber por que algumas pessoas pensam que <…> é mais difícil do que (…) , especialmente considerando struct vs fn . Atualmente, struct tem apenas <…> e fn tem ambos, porque ambos podem ser definidos como parâmetros.
  4. O que eu realmente não gostei de impl Trait na posição arg foi o fato de que é mais difícil ver com o que uma função é realmente definida como parâmetros. Quando você lê foo<A, B> , sabe que há dois parâmetros que resultarão em uma definição e implementação de função.

Eu sinto o que você quer fazer aqui. foo(1, 3) é melhor para você do que foo<3>(1) , mas não para mim. A razão para isso é que foo(1, 3) deve aceitar o segundo argumento para ser baseado em tempo de execução e a proposta que você dá proíbe isso. Posso ver aplicativos, no entanto, especialmente com matrizes, mas atualmente não gosto de sua aparência. Eu seria muito mais para uma solução que diz “se um argumento é const , podemos inferir um tipo de variável / const genérico.

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

Essa é apenas uma sintaxe imaginária que criei para explicar meu ponto: se você passar b como um valor const, N = b e você terminará com sua bela sintaxe:

foo(1, 3)

Se b não for um valor const, é necessário que você passe N explicitamente:

foo<3>(1, x)

Além disso, qual seria a sintaxe para isso, com sua proposta:

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

Isso nos leva a questionar sobre os itens associados, resumindo new() é uma função associada, e deve ser associada ao tipo concreto, const itens genéricos têm semântica como "Este tipo tendo este parâmetro ...", ou seja, tipo dependente. Portanto, new() no contexto não deve ter nenhum argumento. O uso explícito deve ser semelhante a Type<32>::new() . E, claro, todos os argumentos dos genéricos const devem ser elidíveis.

Eu sinto o que você quer fazer aqui. foo (1, 3) é melhor para você do que foo <3> (1), mas não para mim. A razão para isso é que foo (1, 3) deve aceitar o segundo argumento para ser baseado em tempo de execução e a proposta que você dá proíbe isso.

Essa não é minha motivação nem intenção - não estou falando sobre o que "parece melhor" e não acredito que a distinção de tempo de compilação / execução precise ser confundida com a distinção de tipo / nível de valor. Os dois já estão separados- const itens (valor ou fn) certamente não são tipos!

Tudo o que estou obtendo é a conexão semântica que a inferência de tipo traça entre parâmetros de nível de valor e parâmetros de nível de tipo. Não há nenhuma relação com impl Trait - meu ponto, novamente, foi distinguir os argumentos const de impl Trait , precisamente para evitar esse tipo de angústia improdutiva.

Além disso, qual seria a sintaxe para isso, com sua proposta:

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

Essa sintaxe não mudaria! Não estou propondo que removamos os parâmetros const do nível de tipo (sintaticamente ou semanticamente), apenas que os adicionemos ao nível de valor, parcialmente para auxiliar na inferência dos do nível de tipo.

Em cppcon houve uma conversa sobre os parâmetros da função constexpr, que parece semelhante ao que @ PvdBerg1998 está falando e parece o thread interno ao qual @ mark-im está vinculado. Parece que foi bem recebido lá.

Parece uma boa ideia, mas deve ser resolvida depois de concluirmos a implementação inicial de genéricos constantes

CppCon 2019: David Stone - Removendo Metaprogramação de C ++, Parte 1 de N: Parâmetros de Função constexpr

Edit: Isso foi mencionado naquele tópico, aqui está o documento relacionado aos parâmetros da função constexpr
https://github.com/davidstone/isocpp/blob/master/constexpr-parameters.md

Eu gostaria de solicitar que reservássemos a discussão de uma mudança sintática tão significativa para um tópico sobre componentes internos ou um problema (ou alteração do PR) no repo RFCs. A seção de comentários desta edição já é bastante longa e a sintaxe em questão parece desnecessária para um MVP utilizável de genéricos const.

Se pudéssemos mover comentários, provavelmente o faríamos. Podemos bloquear o problema para os membros da equipe @rust-lang e ocultar todos os comentários offtopic?

Todas as discussões de design devem acontecer no repositório RFC e todos os relatórios de bug devem estar em edições separadas.

cc @ rust-lang / moderation Há precedente para fazer isso?

Você pode _converter_ um comentário para um problema, mas não pode mover os comentários. Bloquear é bom, e eu apenas esconderia comentários fora do tópico.

Com o início do novo ano, pensei que seria bom fazer outra atualização sobre onde estamos agora com os genéricos const. Passaram-se pouco mais de 2 anos desde que o RFC const genéricos foi aceito. No primeiro ano (aproximadamente 2018), uma série de grandes esforços de refatoração foram empreendidos para facilitar os genéricos const, melhorando a manipulação de parâmetros genéricos geralmente em todo o compilador. No segundo ano (aproximadamente 2019), o trabalho começou a implementar o próprio const genéricos: primeiro, fazendo com que o mínimo funcionasse e, em seguida, reduzindo a velocidade de melhoria da integridade do recurso. As pessoas também começaram a experimentar o uso de genéricos const no próprio compilador. Descrições mais detalhadas desses esforços estão nos primeiros dois posts de atualização: [1] , [2] .


Houve um bom progresso nos últimos meses desde a última atualização.

  • @ skinny121 :

    • Corrigido um problema de quebra de genéricos constantes em compilação incremental e entre caixas (https://github.com/rust-lang/rust/pull/65652)

    • Correção de um bug com inferência de tipo que afetava genéricos const em diagnósticos (https://github.com/rust-lang/rust/pull/65579)

    • Interface refatorada para avaliação const (https://github.com/rust-lang/rust/pull/66877)

  • @yodaldevoid implementou desambiguação de parâmetros de tipo e const para que os parâmetros const não precisem mais ser incluídos em {} (https://github.com/rust-lang/rust/pull/66104)
  • @eddyb corrigiu o uso de genéricos const como comprimentos de array, uma vez que a normalização preguiçosa é implementada (https://github.com/rust-lang/rust/pull/66883)
  • @varkor :

    • Implementada verificação de structural_match para proibir tipos com verificação de igualdade personalizada de serem usados ​​como genéricos const (https://github.com/rust-lang/rust/pull/65627)

    • Alguns diagnósticos foram corrigidos para levar em consideração os genéricos const (https://github.com/rust-lang/rust/pull/65614)

    • Realizou várias refatorações e correção de inferência de tipo (https://github.com/rust-lang/rust/pull/65643, https://github.com/rust-lang/rust/pull/65660, https: // github. com / rust-lang / rust / pull / 65696)

Um grande obrigado a todos que têm ajudado com os genéricos const!


Qual é o próximo? O maior bloqueador para genéricos const no momento é a normalização preguiçosa , que é necessária para certos tipos de limites genéricos const (como em matrizes ). @ skinny121 está atualmente investigando a normalização preguiçosa, continuando seus esforços fantásticos para eliminar os grandes bugs dos genéricos const. Isso resolveria muitos dos problemas atuais com os genéricos const.

Deixando de lado a normalização preguiçosa, ainda há um número considerável de bugs e cortes de papel que gostaríamos de abordar. Se você estiver interessado em resolver qualquer um dos problemas restantes, sinta-se à vontade para me enviar um ping sobre o assunto, ou no Discord ou Zulip para obter dicas sobre como começar. Estou confiante de que podemos fazer um bom progresso em 2020 e, com sorte, chegar a um ponto em que a estabilização se torne uma discussão viável!

Existe um subconjunto de genéricos const que não atinge a normalização preguiçosa e não tem muitos bugs e cortes de papel, mas que pode expressar uma boa quantidade de código útil? Percebo que impls std de muitas características para matrizes parecem funcionar muito bem. Talvez haja um estreitamento que permitiria que outros engradados escrevessem o tipo de impls que temos em std para suas próprias características, mesmo que eles não suportem todas as funcionalidades mais sofisticadas?

Já estamos mais da metade do ano, então vou resumir brevemente o trabalho que está acontecendo. Muitas pessoas estiveram envolvidas na melhoria do suporte aos genéricos const nos últimos 6 meses, então, obrigado a todos que ajudaram de alguma forma! Eu quero agradecer especialmente a @ skinny121 e @lcnr , que abordaram alguns grandes recursos que faltavam aos generics const: normalização preguiçosa para constantes e suporte a argumentos const em chamadas de método , além de corrigir vários bugs difíceis. Seus esforços são evidentes no resumo abaixo.

Os genéricos Const estão agora sendo usados ​​em vários lugares ao longo do compilador, e já existe experimentação com características e funções fazendo uso de genéricos Const, por exemplo, slice::array_chunks e IntoIterator e FromIterator para [T; N] (https://github.com/rust-lang/rust/pull/65819, https://github.com/rust-lang/rust/pull/69985). A restrição de comprimento 32 para implementações de traços de matriz também está finalmente sendo preparada para remoção .

No momento, estamos discutindo a estabilização de um subconjunto de genéricos const que deve abranger uma ampla gama de casos de uso (embora ainda haja alguns problemas a serem resolvidos antes que os genéricos const possam ser totalmente estabilizados).


  • @ skinny121 :

    • Implementada a primeira versão da normalização preguiçosa (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)

    • Desempenho aprimorado (https://github.com/rust-lang/rust/pull/68118)

    • Vários erros corrigidos (https://github.com/rust-lang/rust/pull/68143)

  • @lcnr :

    • Adicionado suporte para genéricos const explícitos em argumentos de tipo para métodos (https://github.com/rust-lang/rust/pull/74113)

    • Concluída a primeira implementação de normalização preguiçosa para constantes (https://github.com/rust-lang/rust/pull/71973) com @ skinny121

    • Adicionado um lint para unused_braces (https://github.com/rust-lang/rust/pull/70081)

    • Problema corrigido com genéricos const em padrões (https://github.com/rust-lang/rust/pull/70562)

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

    • Impressão bonita aprimorada (https://github.com/rust-lang/rust/pull/72052)

    • Vários erros corrigidos (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)

    • Refatoração (https://github.com/rust-lang/rust/pull/70478)

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

  • @eddyb :

    • Problemas corrigidos com genéricos const em comprimentos de array (https://github.com/rust-lang/rust/pull/70452)

    • Impressão bonita aprimorada (https://github.com/rust-lang/rust/pull/71232)

    • Tratamento de erros aprimorado (https://github.com/rust-lang/rust/pull/71049)

    • Refatoração (https://github.com/rust-lang/rust/pull/70164)

  • @ Aaron1011 : corrigido o erro com o uso de genéricos const cross-crate (https://github.com/rust-lang/rust/pull/72600)
  • @petrochenkov : corrigidos problemas com resolução de nomes e genéricos constantes (https://github.com/rust-lang/rust/pull/70006)
  • @yodaldevoid : corrigido um problema com o uso vitalício em parâmetros genéricos const (https://github.com/rust-lang/rust/pull/74051)
  • @contrun : corrigiu um ICE (https://github.com/rust-lang/rust/pull/69859)
  • @ oli-obk: refatoração (https://github.com/rust-lang/rust/pull/69981)
  • @Centril : diagnóstico aprimorado (https://github.com/rust-lang/rust/pull/70261)
  • @DutchGhost : adicionado um teste (https://github.com/rust-lang/rust/pull/70539)
  • @varkor :

    • Impressão bonita aprimorada (https://github.com/rust-lang/rust/pull/67909, https://github.com/rust-lang/rust/pull/68111)

    • Vários erros corrigidos (https://github.com/rust-lang/rust/pull/67906, https://github.com/rust-lang/rust/pull/68388, https://github.com/rust- lang / rust / pull / 68434)

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

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


Embora os genéricos constantes já tenham percorrido um longo caminho, ainda existem bugs e pontas afiadas . Se você estiver interessado em resolver qualquer um dos problemas restantes, sinta-se à vontade para me enviar um ping, @eddyb sobre o problema, ou no Zulip, para obter dicas sobre como começar. Obrigado!

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