Rust: Problema de rastreamento para identificadores não ASCII (recurso "non_ascii_idents")

Criado em 12 out. 2015  ·  54Comentários  ·  Fonte: rust-lang/rust

Atualmente, os identificadores não ASCII são limitados por recursos. O manuseio deles deve ser corrigido e o portão de recurso removido.

B-unstable C-tracking-issue P-low T-lang

Comentários muito úteis

Não tenho certeza se este é o lugar certo para postar isso, mas alguns problemas interessantes provavelmente aparecerão com linting de símbolos matemáticos. Facilmente evitado escrevendo nomes de variáveis, mas pode ser importante se uma melhor correlação com equações reais for um objetivo.

Por exemplo, Δ (maiúsculas) vs. δ (minúsculas) na captura de tela a seguir. O linter não está /errado/, mas também não faz sentido aplicar o requisito do caso snake aqui.

screen shot 2017-06-27 at 2 28 55 pm

Todos 54 comentários

/cc @rust-lang/lang

nomeando

cc @SimonSapin

Aparentemente, implementamos isso: http://www.unicode.org/reports/tr31/ ou algo parecido.

Eu gostaria de ver isso estabilizado, mas será preciso algum trabalho para nos convencermos de que estamos fazendo a coisa certa.

Eu não tenho ideia do que é a coisa certa aqui. Além das recomendações do Unicode, podemos querer ver o que outras linguagens realmente fazem e quais relatórios de bugs ou críticas relacionadas elas recebem. Ou isso já foi feito quando o recurso foi introduzido pela primeira vez?

@SimonSapin
C e C++ usam http://unicode.org/reports/tr31/#Alternative_Identifier_Syntax (com algumas pequenas restrições) e não vi nenhuma reclamação sobre isso em fóruns isocpp ou listas de problemas :)
Visão geral do problema: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1518.htm
Implementação em Clang: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/UnicodeCharSets.h?view=markup
cc https://github.com/rust-lang/rust/issues/4928

Há também um problema com a normalização de identificadores e mapeamento de nomes unicode mod para os nomes do sistema de arquivos (no OS X, IIRC), mas não consigo encontrar o link relevante aqui: https://github.com /rust-lang/rust/issues/2253. (No pior caso, mod s e extern crate s não-inline podem ser forçados a ser ASCII)

Sim, o número 2253 é o grande problema que conheço que me preocupa com a estabilização prematura de identificadores não-unicode.

(A discussão lá é mais ampla e, sem dúvida, poderia ser dividida em dois segmentos; por exemplo, nós _poderíamos_ pegar um caminho de normalização para identificadores e outro para conteúdo literal de string.)

podemos querer migrar esta discussão para o repositório RFCS, por exemplo, em https://github.com/rust-lang/rfcs/issues/802

Concordo que este é um recurso que merece ser submetido ao processo RFC.

Reaproveitei esse problema para rastrear a estabilização (ou depreciação, etc.) do portão de recurso non_ascii_idents .

Após discussão na reunião da equipe lang, decidimos que sim, um RFC seria o caminho certo a seguir aqui. Precisamos de algo que colete as soluções de outras linguagens, analise seus prós/contras e sugira a escolha apropriada para Rust. Isso é controverso e complexo o suficiente para ser levado à comunidade em geral - especialmente porque muitos de nós hackeando Rust diariamente não temos muita experiência com não-ASCII.

triagem: P-baixo

Marcando tão baixo quanto não há RFC no momento e, portanto, nenhum conteúdo acionável.

Em JavaScript, Perl 5 e Perl 6 este recurso está disponível.
JavaScript (Firefox 50)

function Слово(стойност) {
  this.стойност = стойност;
}
var здрасти = new Слово("Здравей, свят");
console.log(здрасти.стойност) //Здравей, свят

Perl >=5,12

use utf8;
{
  package Слово;
  sub new {
    my $self = bless {}, shift;
    $self->{стойност} = shift;
    $self
  }
};
my $здрасти = Слово->new("здравей, свят");
say ucfirst($здрасти->{стойност}); #Здравей, свят

Perl6 (esta não é apenas a próxima versão do Perl. Esta é uma nova linguagem)

class Слово {
  has $.стойност;
}

my $здрасти = Слово.new(стойност => 'здравей, свят');
say $здрасти.tc; #Здравей, свят

Eu ficaria feliz em vê-lo em Rust também.

Para o que vale a pena, os identificadores no ECMAScript 2015 são baseados na sintaxe do identificador padrão do Unicode Standard Annex #31 .

Perl com use utf8; usa o regexp abaixo, com XID_Start e XID_Continue presumivelmente também do UAX # 31.

/ (?[ ( \p{Word} & \p{XID_Start} ) + [_] ])
        (?[ ( \p{Word} & \p{XID_Continue} ) ]) *    /x

Sim! Obrigado @SimonSapin!

Para Python é <XID_Start> <XID_Continue>* .

Portanto, parece que muitas linguagens de programação que permitem identificadores não ASCII são baseadas no mesmo padrão, mas nos detalhes cada uma delas faz algo um pouco diferente…

Eu pessoalmente adoraria ver suporte para identificadores relacionados à matemática. Por exemplo, ∅ (e operadores de conjunto, como ∩ e ∪). Traduzir equações de artigos/especificações de pesquisa em código é muitas vezes um processo terrível, resultando em código detalhado e difícil de ler. Ser capaz de usar os mesmos identificadores no código que estão nas equações matemáticas do artigo simplificaria a implementação e tornaria o código mais fácil de verificar e comparar com as equações do artigo.

Qual é o ponto deste recurso exatamente? Além de adicionar a possibilidade de criar uma mistura realmente feia de diferentes idiomas em seu código (o inglês é o único idioma verdadeiramente internacional), não oferece benefícios à funcionalidade do idioma. Ou é suporte de unicode para suportar unicode?

@DoumanAsh Nem todo programa é internacional e a fluência em inglês não precisa ser um requisito para a programação.

Não há problema em mantenedores de qualquer projeto decidirem que nomes de variáveis ​​e comentários em seu código devem estar em inglês. Isso é o que acontece com muitos projetos de código aberto, incluindo o próprio rustc. Mas isso não significa que a linguagem deva se restringir a isso.

O caso de uso que vejo não é para escrever código de produção, mas para ensinar. Eu realmente não gosto de dizer às pessoas que elas precisam ser fluentes em inglês para se tornarem programadoras. A outra situação é quando você está escrevendo uma interface do usuário em idioma estrangeiro, se sua interface do usuário tiver uma caixa de texto rotulada "příjmení", mas você acabar colocando o valor em uma variável chamada "sobrenome", o que é estranho. Ainda mais estranho é se você tiver um campo chamado "rodné_číslo" (número de identificação nacional tcheco). Não há palavra análoga em inglês para isso. Então, se eu estivesse escrevendo um aplicativo fiscal tcheco ou um aplicativo bancário, teria que usar um nome estranho sem um bom motivo. Não é como se esse aplicativo fosse portátil para outros idiomas de qualquer maneira.

Outra boa razão para permitir isso é que os linguistas geralmente precisam usar a notação IPA em nomes de variáveis. Os nomes em inglês dos símbolos IPA podem ser rediculamente longos. Por exemplo, o som r do inglês americano na palavra red é transcrito como um caractere ɹ̠, mas é denominado aproximante retroflexivo pós-alveolar. https://en.wikipedia.org/wiki/Alveolar_and_postalveolar_approximants Portanto, se estou escrevendo um programa de conversão de texto em fala, talvez queira escrever fn say_ɹ̠() em vez de fn say_post_alveolar_retroflexive_approximant() .

Do lado da não opinião, acho que há uma discussão interessante a ser feita aqui em relação a quais pontos de código unicode podem fazer parte de um nome de variável. Por exemplo: Posso nomear uma variável price€ ? Provavelmente não, eu não acho que price$ funciona, não é? Posso criar uma macro →![] para gerar vetores? Eu sei que alguém pode querer fazer isso, mas → é um "símbolo matemático" http://www.fileformat.info/info/unicode/char/2192/index.htm . Portanto, ao usar o lexing, precisamos tomar uma decisão sobre quais pontos de código são aceitáveis ​​e quais não são, e talvez a ferrugem não deva simplesmente perguntar estupidamente ao padrão unicode se algo é uma letra ou não.

@timthelion na implementação atual Rust não pergunta estupidamente ao padrão unicode se algo é uma letra ou não - ele se baseia nas propriedades unicode XID_Start e XID_Continue que têm um comportamento correto e intuitivo em todos os seus exemplos.

  • say_ɹ̠ é permitido porque 'ɹ' e '̠' são XID_Continue.
  • price€ e price$ não são permitidos porque '€' e '$' não são XID_Continue.
  • →![] não é permitido porque '→' não é XID_Start.
  • příjmení e rodné_číslo são permitidos.

@dtolnay obrigado pela explicação. Espero que você não tenha se ofendido com meu uso da palavra "burra", talvez tenha sido uma palavra mal escolhida.

Não, apenas apontando que grandes mentes pensam da mesma forma e o bom pessoal do comitê técnico do unicode tinha as mesmas preocupações que você.

Posso criar outros casos de uso em produção.

Algumas palavras em um campo específico são difíceis de traduzir para o inglês, mas alguns programas (por exemplo, jogos, serviços locais online para offline) podem ter que lidar, por exemplo, nomes de pratos chineses, nomes de heróis, nomes de lugares. Os programadores que trabalham para empresas não precisam saber quais são as traduções em inglês, mas precisam fornecer seus nomes de variáveis ​​e funções. Eles inventarão nomes estranhos se tiverem que usar o inglês, geralmente muito difícil para outros colegas de trabalho entenderem.

Neste ponto, acho que não há dúvida de que existem muitos casos de nós. O que resta a fazer é descobrir os detalhes:

  • Quais caracteres exatamente devem ser permitidos. Por exemplo, pontuação não ASCII provavelmente deve ser excluída.
  • Quanta normalização deve ser feita: dois identificadores podem ser representados com diferentes pontos de código (diferentes bytes UTF-8 nos arquivos de origem), mas ainda assim serem considerados equivalentes.

Vários outros idiomas concordam com o Unicode Standard Annex # 31, mas têm pequenas diferenças nos detalhes. Idealmente, descobriríamos o que motivou essas diferenças para decidir o que é melhor para Rust.

https://rosettacode.org/wiki/Unicode_variable_names tem algumas informações para muitos idiomas.

Eu concordo com @SimonSapin - ninguém duvida que isso seria útil. O problema é que não existe uma solução padrão e muitos de nós (por exemplo, eu mesmo) estamos em uma posição ruim para avaliar as compensações. O que está faltando é alguém para coletar as restrições e fazer uma recomendação, eu suspeito. Suspeito que neste momento qualquer decisão seria preferível a nenhuma decisão - embora eu definitivamente prefira seguir algum precedente (idealmente, uma especificação ou anexo unicode, mas talvez também outro lang) do que apenas adotar um outro conjunto de regras.

@nikomatsakis Seria bom pesquisar exatamente o que motivou as pequenas diferenças entre vários idiomas, mas se ninguém fizer essa pesquisa e quisermos prosseguir de qualquer maneira, acho que seguir o UAX # 31 exatamente (o que acredito ser o nosso implementação atual faz) é um bom padrão.

Ainda pode valer a pena passar pelo processo RFC com um design detalhado, mesmo que corresponda à implementação atual. (Quais caracteres podem ser usados, como eles são normalizados / comparados para equivalência, como lidamos com futuras versões de Unicode, etc.) Sugiro que quem escreve este RFC leia UAX 31 de cima para baixo pelo menos uma vez.

Também podemos considerar a criação de um novo (ou, mais provavelmente, usando um subconjunto restrito de um dos perfis existentes) perfil PRECIS [1] para identificadores. Isso nos permitiria normalizar identificadores que devem ser considerados iguais mesmo que sejam ligeiramente diferentes (por exemplo, para localidades que possuem teclados que produzem texto que parece o mesmo, mas difere ligeiramente em sua representação Unicode), bem como fornecer uma clara e conciso conjunto de regras para determinar o que é um identificador Rust válido.

Não estou ciente de nenhuma implementação Rust existente da estrutura PRECIS (muito da infraestrutura Unicode necessária para criar uma ainda está faltando, eu acho, mas isso provavelmente teria que ser corrigido de qualquer maneira).

Eu não me chamaria de especialista, mas ajudei a construir uma implementação do PRECIS e geralmente estou familiarizado com os RFCs e algumas das armadilhas e armadilhas, então ficaria feliz em ajudar (ou bugar o grupo de trabalho do PRECIS para obter ajuda) onde necessário.

[1] [RFC 7564](https://tools.ietf.org/html/rfc7564): PRECIS Framework: Preparação, Aplicação e Comparação de Strings Internacionalizadas em Protocolos de Aplicativos

Bom ponto sobre os personagens que parecem iguais. Aqui está a wikipédia
artigo sobre o assunto
https://en.wikipedia.org/wiki/Duplicate_characters_in_Unicode

Aqui está um artigo que explica que os caracteres duplicados de
scripts asiáticos são principalmente unificados:

https://people.w3.org/risshida/scripts/chinese/

Em 11/04/2017 21:01, Sam Whited escreveu:
>

Também podemos considerar a criação de um novo (ou, mais provavelmente, usando um
subconjunto restrito de um dos perfis existentes) Perfil PRECIS [1]
para identificadores. Isso nos permitiria normalizar identificadores que
devem ser considerados iguais mesmo que sejam ligeiramente diferentes (ex.
para localidades que possuem teclados que produzem texto com a mesma aparência,
mas difere ligeiramente em sua representação Unicode), bem como fornecer
um conjunto claro e conciso de regras para determinar o que é um Rust válido
identificador.

Não tenho conhecimento de nenhuma implementação Rust existente do PRECIS
framework (muito da infraestrutura Unicode necessária para criar um
ainda está faltando, eu acho, mas isso provavelmente teria que ser corrigido
um pouco de qualquer maneira).

[1] RFC 7564 https://tools.ietf.org/html/rfc7564 : Estrutura PRECIS:
Preparação, Aplicação e Comparação de Strings Internacionalizadas
em Protocolos de Aplicação


Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/rust-lang/rust/issues/28979#issuecomment-293367700 ,
ou silenciar o thread
https://github.com/notifications/unsubscribe-auth/ABU7-IMgXefW2yZYyM0tn8qLhpGFw0bSks5ru84GgaJpZM4GM3Lj .

@SamWhited Por que PRECIS sobre NFC ou NFKC do Unicode?

Por que PRECIS sobre NFC ou NFKC do Unicode?

TL:DR — A normalização é apenas uma etapa que gostaríamos de fazer ao determinar se algo é um identificador válido. Outras operações podem (ou não) também precisar ser executadas.

@SimonSapin A normalização Unicode é apenas uma etapa de um perfil PRECIS (portanto, estaríamos de fato usando normalização; provavelmente NFC em um palpite), no entanto, PRECIS abrange uma variedade mais ampla de coisas. Por exemplo, os formulários de normalização não fazem mapeamento de largura (eu não acho?), então FullWidth não será o mesmo identificador que FullWidth . Se você estiver em um teclado que deseja digitar texto de largura total, isso pode ser um problema (provavelmente é mais um problema com caracteres do leste asiático do que com caracteres latinos, mas talvez alguém de uma localidade que use texto de largura total possa entre em contato e me diga se estou deturpando o problema de alguma forma). Outras coisas que um perfil PRECIS pode fazer incluem definir um subconjunto de propriedades de caracteres que são permitidas (por exemplo, letras, números, traços e começa com uma letra ou algo parecido).

_Isenção de responsabilidade:_ Na verdade, não pensei se o mapeamento de texto de largura total seria desejável ou não; é apenas um exemplo. Pode muito bem ser que a normalização seja tudo o que importa, ou talvez não queiramos fazer nenhum mapeamento; Go só verifica se os identificadores têm a propriedade de letra ou número, eu acho, então se eles se dão bem com apenas isso, talvez seja bom para nós também. Mais reflexão é certamente necessária.

Leitura adicional: é isso que a especificação Go faz (que é muito mais simples do que sugeri, o que pode ou não ser uma coisa boa): https://golang.org/ref/spec#Source_code_representation

O que usa PRECIS? Qualquer linguagem de programação?

O que usa PRECIS? Qualquer linguagem de programação?

Não tenho certeza do que qualquer idioma além do Go faz .

Problema relacionado ao Go 2: golang/go#16033

Em terça-feira, 11 de abril de 2017 às 14:07:49 -0700, Sam Whited escreveu:

Por exemplo, os formulários de normalização não fazem mapeamento de largura, então FullWidth não será o mesmo identificador que FullWidth .

NFKC faz isso:

Python 3.6.0 (default, Jan 16 2017, 12:12:55)
[GCC 6.3.1 20170109] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> FullWidth = 1
>>> FullWidth
1

--
Atenciosamente,
lilydjwg

@SamWhited , no seu primeiro link eu encontro:

identifier = letter { letter | unicode_digit } .
letter        = unicode_letter | "_" .

Mas até onde eu sei, o Go atualmente não faz nenhuma normalização e usar o PRECIS é uma proposta. Isso é correto?

Mas até onde eu sei, o Go atualmente não faz nenhuma normalização e usar o PRECIS é uma proposta. Isso é correto?

@SimonSapin está correto; bem, nem mesmo uma proposta, apenas uma ideia para ser pensada como esta questão (desculpe, releia essa frase e meu link e estava mal redigida; não queria sugerir que ela use agora, apenas que eu não sei o que outra coisa além de Go realmente faz para lidar com identificadores não ASCII).

@SimonSapin

Ainda pode valer a pena passar pelo processo RFC com um design detalhado, mesmo que corresponda à implementação atual.

👍

Eu estava lendo o UAX # 31 para ver o que eles fizeram, e outro benefício de usar um perfil PRECIS se destacou para mim: assim como depreciar stringprep e usar PRECIS, ele fornece uma maneira de ser compatível e ágil no futuro em versões Unicode ( operando em propriedades derivadas de pontos de código em vez de pontos de código individuais).

Embora o TR31 tenha um conceito de " identificadores imutáveis " para ajudar a resolver isso, ele é efetivamente uma versão um pouco menos restritiva de um protocolo PRECIS derivado da classe de forma livre, mas sem as considerações que o PRECIS deu à ordem em que as regras precisam ser aplicado (eu não acho?) também não cobre casos extremos cobertos pela estrutura PRECIS, como o uso do sigma final grego, ou alguns dos casos extremos em torno do Hangul Jamo (novamente, não sou especialista em nenhum desses , mas é por isso que o PRECIS existe; os especialistas já fizeram o trabalho).

ele fornece uma maneira de ser compatível com o futuro e ágil em versões Unicode (operando em propriedades derivadas de pontos de código em vez de pontos de código individuais).

Não entendo este ponto. XID_Start e XID_Continue são propriedades derivadas.

Não entendo este ponto. XID_Start e XID_Continue são propriedades derivadas.

Eu posso ter entendido mal o UAX 31 então; pareceu-me que exigia uma versão específica do Unicode. Relendo, não consigo ver de onde tirei isso.

Não tenho certeza se este é o lugar certo para postar isso, mas alguns problemas interessantes provavelmente aparecerão com linting de símbolos matemáticos. Facilmente evitado escrevendo nomes de variáveis, mas pode ser importante se uma melhor correlação com equações reais for um objetivo.

Por exemplo, Δ (maiúsculas) vs. δ (minúsculas) na captura de tela a seguir. O linter não está /errado/, mas também não faz sentido aplicar o requisito do caso snake aqui.

screen shot 2017-06-27 at 2 28 55 pm

seria possível permitir emoji em nomes de variáveis ​​mesmo que não sejam XID Start/Continue, como no Swift?

@fwrs , Emojis são muito mais complicados agora do que personagens que não são Emoji.

Graças a alguns fornecedores, agora você pode ter sequências de junção de Emoji (ZWJ) que continuam mudando suas cores e pequenos detalhes, muitos dos quais não são necessariamente visíveis a olho nu.

Além disso, a definição de Emoji está se expandindo rapidamente, a cada ano, o que não é algo que uma linguagem de programação de nível de sistema queira ser estável e confiável.

Então, embora seja fofo, não acho que se encaixe bem com os objetivos do Rust. Mas, linguagens de script/educação baseadas em ferrugem podem se beneficiar ao permitir Emojis, dependendo de seus objetivos.

@ryankurte Há um problema semântico em seu exemplo - você está transcrevendo fórmulas matemáticas, mas usou U + 0394 GREGO CAPITAL LETTER DELTA em vez de U + 2206 INCREMENT. A primeira é uma letra do alfabeto grego e, como tal, possui mapeamento de maiúsculas e minúsculas; o último é um símbolo matemático e não.

Eu gostaria de fazer um link cruzado deste comentário: https://github.com/rust-lang/rust/issues/4928#issuecomment -343137316

Não vi a possibilidade de habilitar ataques baseados em homoglifos aqui (se alguém os mencionou, ignore o ruído), mas acabei de preencher um problema recortado para solicitar um lint que avisa sobre código como este:

#![feature(non_ascii_idents)]
fn main() {
    let a = 2;
    let а = 3;
    assert_eq!(a, 2);  // OK
    assert_eq!(а, 3);  // OK
}

Em poucas palavras, esses dois a s são caracteres unicode diferentes, de modo que a segunda ligação let não sombreia a primeira, e ambas as declarações passam (o playground não parece suportar identificadores unicode, portanto, a única maneira de tente isso localmente; funciona para mim).

Esse "recurso" pode ser usado para introduzir exploits em programas Rust que são mais difíceis de detectar, em particular porque as ligações de sombras let são consideradas Rust idiomáticas por muitos, inclusive eu.

PS: esse "recurso" pode ser útil em concursos de Rust dissimulados, embora esse #![feature(non_ascii_idents)] deva levantar algumas sobrancelhas :)

@gnzlbg Acredito que já exista algum suporte para detecção de confundíveis para impedir que as pessoas troquem seus pontos e vírgulas por pontos de interrogação gregos e tal, mas não sei se isso se aplica a identificadores. Se isso acontecer, então isso resolve o problema; se não, pelo menos temos o ferramental para fazê-lo pronto para ir.

Estou um pouco preocupado que este seja um candidato a ser fechado e o código removido do compilador porque não teve movimento significativo por um tempo e requer um RFC. Eu me importo bastante com o Rust ser uma linguagem do século 21, o que significa Unicode, e com o Rust ser amigável para programadores que não falam inglês. O que me falta é a capacidade de realmente escrever um RFC.

@Ketsuban

Acredito que já exista algum suporte para detecção de confundíveis para impedir que as pessoas troquem seus pontos e vírgulas por pontos de interrogação gregos e tal, mas não sei se isso se aplica a identificadores.

sim, acho que, como sugerido por @oli-obk na edição do clippy, a implementação do Rust usaria apenas a lista oficial mais recente que pode ser confundida:

http://www.unicode.org/Public/security/revision-06/confusables.txt

ataques baseados em homóglifos podem ser evitados. Essa lista precisaria ser mantida em sincronia, mas isso é algo que pode ser automatizado como parte do sistema de compilação.

@Ketsuban

Se você se importa com isso, existem outras linguagens que suportam unicode em seus identificadores, e essas linguagens possuem processos semelhantes ao processo RFC. Você pode começar verificando aqueles. Quem sabe, talvez você possa simplesmente mesclá-los com o feedback desta edição e obter uma pré-RFC no fórum interno? A partir desse ponto, trata-se apenas de incorporar/argumentar o feedback com os outros e, antes que você perceba, terá um RFC pronto.

De certa forma, espero que fiquemos com identificadores ASCII para sempre. Manipular identificadores unicode é uma grande dor de interoperabilidade. Alguns dos exemplos mais bizarros de mapeamentos NFKC é que coisas como este mapeiam para o mesmo identificador:

>>> ℌ = 1
>>> H
1
>>> Ⅸ = 42
>>> IX
42
>>> ℕ = 23
>>> N
23
>>> import math
>>> ℯ = math.e
>>> e
2.718281828459045
>>> ℨ = 2
>>> Z
2

@mitsuhiko O mundo real tem esse tipo de dor. Não podemos simplesmente ignorar esse problema porque é difícil de lidar e envolve um recurso para o qual você _pessoalmente_ não tem uso.

Além disso, a RFC atual propõe explicitamente NFC sobre NFKC, após uma _lot_ de discussão sobre exemplos muito semelhantes a esses.

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