Rust: 非 ASCII 标识符的跟踪问题(功能“non_ascii_idents”)

创建于 2015-10-12  ·  54评论  ·  资料来源: rust-lang/rust

非 ASCII 标识符当前是功能门控的。 应该修复它们的处理并移除特征门。

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

最有用的评论

不确定这是否是发布此内容的正确位置,但一些有趣的问题可能会随着数学符号的出现而出现。 通过写出变量名称很容易避免,但如果与实际方程更好的相关性是目标,则可能很重要。

例如,以下屏幕截图中的 Δ(大写)与 δ(小写)。 linter 不是 /wrong/,但 imo 在这里应用蛇盒要求也没有任何意义。

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

所有54条评论

/cc @rust-lang/lang

提名

抄送@SimonSapin

显然我们实现了这个: http ://www.unicode.org/reports/tr31/ 或类似的东西。

我希望看到这种情况稳定下来,但需要一些工作才能说服自己我们正在做正确的事情。

我不知道什么是正确的。 除了 Unicode 建议之外,我们可能还想看看其他语言实际做了什么,以及它们得到了哪些相关的错误报告或批评。 还是在首次引入该功能时已经完成?

@SimonSapin
C 和 C++ 使用http://unicode.org/reports/tr31/#Alternative_Identifier_Syntax (有一些小的限制),我没有在 isocpp 论坛或问题列表上看到任何关于它的投诉 :)
问题概述: http ://www.open-std.org/jtc1/sc22/wg14/www/docs/n1518.htm
Clang 中的实现: http ://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/UnicodeCharSets.h?view=markup
抄送https://github.com/rust-lang/rust/issues/4928

标识符的规范化和将 unicode mod名称映射到文件系统名称(在 OS X,IIRC 上)也存在问题,但我在这里找不到相关链接https ://github.com mod s 和extern crate s 可以强制为 ASCII)

是的 #2253 是我知道的一个大问题,它让我担心非 unicode 标识符的过早稳定。

(那里的讨论范围更广,可以说可以分为两个线程;例如,我们_可以_为标识符采用一个规范化路径,为字符串文字内容采用另一个规范化路径。)

我们可能希望将此讨论迁移到RFCS repo,例如https://github.com/rust-lang/rfcs/issues/802

我同意这是一个值得通过 RFC 流程进行的功能。

我已将此问题重新用于跟踪non_ascii_idents功能门的稳定性(或弃用等)。

在 lang 团队会议上讨论后,我们决定是的,RFC 将是正确的前进方式。 我们需要一些东西来收集其他语言的解决方案,分析它们的优缺点,并为 Rust 提出合适的选择。 这是有争议且足够复杂的,应该把它带到整个社区——尤其是我们中的许多人每天都在使用 Rust 进行黑客攻击,无论如何都没有太多使用非 ASCII 的经验。

分诊:P-低

标记为低至目前没有 RFC,因此没有可操作的内容。

在 JavaScript、Perl 5 和 Perl 6 中,此功能可用。
JavaScript (火狐 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(这不仅仅是 Perl 的下一个版本。这是一种新语言)

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

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

我也很高兴在 Rust 中看到它。

值得一提的是,ECMAScript 2015中的标识符基于Unicode 标准附件 #31中的默认标识符语法。

带有use utf8;的 Perl 使用下面的正则表达式, XID_StartXID_Continue大概也来自 UAX #31。

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

是的! 谢谢@SimonSapin!

对于 Python ,它是<XID_Start> <XID_Continue>*

因此,看起来许多允许非 ASCII 标识符的编程语言都基于相同的标准,但在细节上它们各自做的事情略有不同……

我个人希望看到对数学相关标识符的支持。 例如,∅(和集合运算符,如 ∩ 和 ∪)。 将研究论文/规范中的方程式转换为代码通常是一个糟糕的过程,导致代码冗长且难以阅读。 能够在代码中使用与论文数学方程式中相同的标识符将简化实现,并使代码更容易检查和与论文的方程式进行比较。

这个功能到底有什么意义? 除了增加在您的代码中创建真正丑陋的不同语言组合的可能性(英语是唯一真正的国际语言)之外,它对语言功能没有任何好处。 还是为了支持unicode而支持unicode?

@DoumanAsh并非每个程序都是国际化的,并且英语流利不一定是编程的要求。

任何项目的优秀维护者都可以决定其代码中的变量名和注释应该是英文的。 许多开源项目都会发生这种情况,包括 rustc 本身。 但这并不意味着语言应该仅限于此。

我看到的用例不是用于编写生产代码,而是用于教学。 我真的很糟糕告诉人们他们必须精通英语才能成为程序员。 另一种情况是,当您编写外语 UI 时,如果您的 UI 有一个标有“příjmení”的文本框,但您最终将值放在一个名为“last name”的变量中,这很奇怪。 更奇怪的是,如果您有一个名为“rodné_číslo”(捷克国民身份证号码)的字段。 没有类似的英语单词。 因此,如果我正在编写捷克税务应用程序或银行应用程序,我将不得不无缘无故地使用一个奇怪的名称。 无论如何,它不像这样的应用程序可以移植到其他语言。

另一个很好的理由是语言学家经常需要在变量名中使用 IPA 表示法。 国际音标符号的英文名称可能非常长。 例如,美式英语单词 red 中的 r 音被转写为一个字符 ɹ̠,但被命名为后齿槽反折逼近音。 https://en.wikipedia.org/wiki/Alveolar_and_postalveolar_approximants因此,如果我正在编写文本转语音程序,我可能有理由想编写fn say_ɹ̠()而不是fn say_post_alveolar_retroflexive_approximant()

在没有意见的方面,我认为这里有一个有趣的讨论,关于允许哪些 unicode 代码点成为变量名的一部分。 例如:我可以命名一个变量price€吗? 可能不是,我认为price$行不通吗? 我可以创建一个 →![] 宏来生成向量吗? 我知道有人可能想这样做,但 → 是一个“数学符号” http://www.fileformat.info/info/unicode/char/2192/index.htm 。 因此,在进行词法分析时,我们需要决定哪些代码点是可以接受的,哪些是不可接受的,也许 rust 不应该简单地愚蠢地询问 unicode 标准是否是字母。

@timthelion在当前的实现中,Rust 不会简单地询问 unicode 标准是否是字母 - 它依赖于XID_StartXID_Continue unicode 属性,这些属性在您的所有示例中都具有正确和直观的行为。

  • say_ɹ̠是允许的,因为'ɹ''̠'都是 XID_Continue。
  • 不允许使用price€price$ ,因为'€''$'不是 XID_Continue。
  • 不允许使用→![] ,因为'→'不是 XID_Start。
  • příjmenírodné_číslo是允许的。

@dtolnay谢谢你的解释。 我希望你不会因为我使用“愚蠢”这个词而被冒犯,也许这是一个选择不当的词。

不,只是指出伟大的思想是相似的,而 unicode 技术委员会的优秀人员与您有同样的担忧。

我可以提出其他生产中的用例。

有些特定领域的词很难翻译成英文,但有些程序(如游戏、本地在线到离线服务)可能需要处理,如中国菜名、英雄名、地名。 为公司工作的程序员不需要知道英文翻译是什么,但他们必须给出他们的变量和函数名称。 如果他们必须使用英语,他们会想出奇怪的名字,通常其他同事很难理解。

在这一点上,我认为毫无疑问我们有很多案例。 剩下要做的是弄清楚细节:

  • 究竟应该允许哪些字符。 例如,可能应该排除非 ASCII 标点符号。
  • 应该进行多少规范化:两个标识符可以用不同的代码点(源文件中的不同 UTF-8 字节)表示,但仍被认为是等效的。

其他几种语言同意 Unicode 标准附件 # 31,但在细节上略有不同。 理想情况下,我们会找出造成这些差异的原因,以便决定什么对 Rust 最有利。

https://rosettacode.org/wiki/Unicode_variable_names有许多语言的一些信息。

我同意@SimonSapin - 没有人怀疑这会有用。 问题是没有标准解决方案,我们中的许多人(例如,我自己)在评估权衡取舍方面处于不利地位。 我怀疑,我们缺少的是收集约束并提出建议的人。 在这一点上,我怀疑任何决定都比没有决定更可取——尽管我肯定更愿意遵循一些先例(理想情况下,一个 unicode 规范或附件,但也可能是另一种语言),而不是仅仅采用另一套规则。

@nikomatsakis很高兴能准确研究是什么导致了各种语言之间的微小差异,但如果没有人站出来进行这项研究并且我们仍想继续,那么我认为完全遵循 UAX #31(我相信这是我们的当前实现确实)是一个很好的默认值。

即使它恰好与当前的实现相匹配,也可能仍然值得通过详细设计来完成 RFC 流程。 (可以使用哪些字符,如何对它们进行规范化/等价比较,我们如何处理未来的 Unicode 版本等)我建议编写此 RFC 的人至少从上到下阅读 UAX 31 一次。

我们可能还想考虑为标识符创建一个新的(或者更有可能使用现有配置文件之一的受限子集)PRECIS 配置文件 [1]。 这将允许我们规范化应该被视为相同的标识符,即使它们略有不同(例如,对于具有键盘的语言环境,其输出的文本看起来相同,但其 Unicode 表示形式略有不同),并提供一个清晰的和一组简洁的规则来确定什么是有效的 Rust 标识符。

我不知道 PRECIS 框架的任何现有 Rust 实现(我认为创建一个所需的许多 Unicode 基础设施仍然缺失,但这可能必须以某种方式修复)。

我不会称自己为专家,但我帮助构建了一个 PRECIS 实现,并且通常熟悉 RFC 以及一些陷阱和陷阱,所以我很乐意提供帮助(或向 PRECIS 工作组寻求帮助)需要的地方。

[1] [RFC 7564](https://tools.ietf.org/html/rfc7564):PRECIS 框架:应用协议中国际化字符串的准备、执行和比较

关于看起来相同的字符的好点。 这是维基百科
关于这个问题的文章
https://en.wikipedia.org/wiki/Duplicate_characters_in_Unicode

这是一篇文章,它解释了来自的重复字符
亚洲文字大多是统一的:

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

在 2017 年 4 月 11 日晚上 9:01,山姆·怀特德写道:
>

我们也可以考虑创建一个新的(或者,更有可能,使用
现有配置文件之一的受限子集)PRECIS 配置文件 [1]
用于标识符。 这将允许我们规范化标识符
即使它们略有不同(例如。
对于具有键盘输出看起来相同的文本的语言环境,
但其 Unicode 表示略有不同)以及提供
一套清晰简洁的规则来确定什么是有效的 Rust
标识符。

我不知道 PRECIS 的任何现有 Rust 实现
框架(创建一个框架所需的大量 Unicode 基础设施
我认为仍然失踪,但这可能必须修复
无论哪种方式)。

[1] RFC 7564 https://tools.ietf.org/html/rfc7564:PRECIS框架:
国际化字符串的准备、执行和比较
在应用协议


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/rust-lang/rust/issues/28979#issuecomment-293367700
或使线程静音
https://github.com/notifications/unsubscribe-auth/ABU7-IMgXefW2yZYyM0tn8qLhpGFw0bSks5ru84GgaJpZM4GM3Lj

@SamWhited为什么 PRECIS 优于 Unicode 的 NFC 或 NFKC?

为什么 PRECIS 优于 Unicode 的 NFC 或 NFKC?

TL:DR — 规范化只是我们在确定某物是否是有效标识符时想要做的一个步骤。 其他操作可能(或可能不)也需要执行。

@SimonSapin Unicode 规范化只是 PRECIS 配置文件的一个步骤(因此我们实际上会使用规范化;猜测可能是 NFC),但是,PRECIS 涵盖了更广泛的内容。 例如,规范化表单不进行宽度映射(我不认为?),所以FullWidth不会是与FullWidth相同的标识符。 如果您使用的是想要输入全角文本的键盘,这可能是一个问题(这可能是东亚字符的问题,而不是拉丁字符,但也许来自使用全角文本的语言环境的人可以插话并告诉我我是否以任何方式歪曲了这个问题)。 PRECIS 配置文件可以做的其他事情包括定义允许的字符属性的子集(例如,字母、数字、破折号以及以字母或类似内容开头)。

_免责声明:_我实际上并没有考虑是否需要映射全宽文本; 这只是一个例子。 很可能归一化才是最重要的,或者我们根本不关心做任何映射; 我认为,Go 只检查标识符是否具有字母或数字属性,所以如果它们仅通过这些属性,也许对我们来说也很好。 当然需要更多的思考。

进一步阅读:这就是 Go 规范所做的(这比我建议的要简单得多,这可能是也可能不是一件好事): https ://golang.org/ref/spec#Source_code_representation

什么使用 PRECIS? 有没有编程语言?

什么使用 PRECIS? 有没有编程语言?

我不确定除了Go之外的任何语言。

相关 Go 2 问题:golang/go#16033

2017 年 4 月 11 日,星期二,下午 02:07:49 -0700,Sam Whited 写道:

例如,规范化表单不进行宽度映射,因此FullWidth不会是与FullWidth相同的标识符。

NFKC 这样做:

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

--
最好的祝福,
lilydjwg

@SamWhited ,在您的第一个链接中,我发现:

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

但据我所知,Go 目前没有进行任何规范化,使用 PRECIS 是一个提议。 那是对的吗?

但据我所知,Go 目前没有进行任何规范化,使用 PRECIS 是一个提议。 那是对的吗?

@SimonSapin是正确的; 好吧,甚至不是一个真正的提议,只是一个像这个问题一样需要思考的想法(对不起,重读那句话和我的链接,它的措辞很糟糕;并不意味着它现在确实使用它,只是我不知道除了 Go 之外的任何东西实际上是如何处理非 ASCII 标识符的)。

@SimonSapin

即使它恰好与当前的实现相匹配,也可能仍然值得通过详细设计来完成 RFC 流程。

👍

我只是在阅读UAX #31以了解他们做了什么,使用 PRECIS 配置文件的另一个好处对我来说很突出:就像弃用 stringprep 并改用 PRECIS 一样,它提供了一种在 Unicode 版本之间实现未来兼容和敏捷的方法(通过对代码点的派生属性而不是单个代码点本身进行操作)。

虽然 TR31 确实有一个“不可变标识符”的概念来帮助解决这个问题,但它实际上从自由格式类派生的 PRECIS 协议的限制性稍低的版本,但没有考虑 PRECIS 对规则需要的顺序的考虑。应用(我不认为?)它也不涵盖 PRECIS 框架涵盖的边缘情况,例如使用希腊最终 sigma 或 Hangul Jamo 周围的一些边缘情况(再次,我不是这两个方面的专家,但这就是 PRECIS 存在的原因;专家们已经完成了这项工作)。

它提供了一种跨 Unicode 版本的未来兼容和敏捷方法(通过操作代码点的派生属性而不是单个代码点本身)。

我不明白这一点。 XID_StartXID_Continue是派生属性。

我不明白这一点。 XID_Start 和 XID_Continue 是派生属性。

那我可能误解了UAX 31; 在我看来,它需要一个特定的 Unicode 版本。 重读我看不到我从哪里得到的。

不确定这是否是发布此内容的正确位置,但一些有趣的问题可能会随着数学符号的出现而出现。 通过写出变量名称很容易避免,但如果与实际方程更好的相关性是目标,则可能很重要。

例如,以下屏幕截图中的 Δ(大写)与 δ(小写)。 linter 不是 /wrong/,但 imo 在这里应用蛇盒要求也没有任何意义。

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

是否可以在变量名中允许表情符号,即使它们不是 XID 开始/继续,就像在 Swift 中一样?

@fwrs ,现在表情符号比非表情符号字符复杂得多。

感谢一些供应商,现在您可以让表情符号加入 (ZWJ) 序列不断改变颜色和小细节,其中许多不一定是肉眼可见的。

此外,Emoji 的定义每年都在快速扩展,这不是系统级编程语言想要稳定可靠的需求。

所以,虽然它很可爱,但我认为它不适合 Rust 目标。 但是,基于 rust 的脚本/教育语言可能会受益于允许表情符号,这取决于他们的目标。

@ryankurte您的示例中存在语义问题-您正在转录数学公式,但您使用的是 U+0394 GREEK CAPITAL LETTER DELTA 而不是 U+2206 INCREMENT。 前者是希腊字母,因此有大小写映射; 后者是一个数学符号,而不是。

我想交叉链接此评论: https ://github.com/rust-lang/rust/issues/4928#issuecomment -343137316

我还没有看到在这里启用基于同形字符的攻击的可能性(如果有人提到它们,请忽略噪音),但我只是填写了一个简短的问题来请求一个 lint,它会在这样的代码上发出警告:

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

简而言之,这两个a是不同的 unicode 字符,因此第二个 let 绑定不会影响第一个,并且两个断言都通过(尽管游乐场似乎不支持 unicode 标识符,但这是唯一的方法试试这是本地的;对我有用)。

这个“特性”可用于在 Rust 程序中引入更难检测的漏洞,特别是考虑到阴影 let 绑定被包括我在内的许多人认为是惯用的 Rust。

PS:这个“特性”可能在不为人知的 Rust 比赛中很有用,尽管#![feature(non_ascii_idents)]应该引起一些人的注意 :)

@gnzlbg相信已经有一些对混淆检测的支持,以阻止人们将分号换成希腊问号等,但我不知道它是否适用于标识符。 如果是这样,那就解决了这个问题; 如果没有,至少我们有工具可以完成它。

我有点担心这是一个被关闭的候选者,并且代码从编译器中删除,因为它有一段时间没有明显的移动并且需要 RFC。 我相当关心 Rust 是 21 世纪的语言,这意味着 Unicode,以及 Rust 对非英语程序员友好。 我缺乏的是实际编写 RFC 的能力。

@Ketsuban

我相信已经有一些对混淆检测的支持,以阻止人们将分号换成希腊问号等,但我不知道它是否适用于标识符。

是的,我认为,正如@oli-obk 在clippy issue 中所建议的那样,Rust 实现将改为使用最新的官方可混淆列表:

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

可以防止基于同形字的攻击。 这个列表需要保持同步,但这是可以作为构建系统的一部分自动化的。

@Ketsuban

如果您关心这一点,还有其他语言在其标识符中支持 unicode,并且这些语言具有类似于 RFC 流程的流程。 你可以从检查这些开始。 谁知道呢,也许您可​​以将它们与本期的反馈合并在一起,然后在内部论坛中获得一个 pre-RFC? 从那时起,它只是与其他人合并/争论反馈,并且在您知道之前,您将准备好 RFC。

在某种程度上,我希望我们永远坚持使用 ASCII 标识符。 处理 unicode 标识符是一个巨大的互操作性难题。 NFKC 映射的一些更奇怪的例子是,像这样的东西映射到相同的标识符:

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

@mitsuhiko现实世界有那种痛苦。 我们不能忽略这个问题,因为它很难处理并且涉及到您_个人_没有用的功能。

此外,在对与这些非常相似的示例进行了大量讨论之后,当前的 RFC 明确提出了 NFC over NFKC。

此页面是否有帮助?
0 / 5 - 0 等级