Rust: (模块)作为可见性修饰符的 `crate` 的跟踪问题

创建于 2018-08-06  ·  91评论  ·  资料来源: rust-lang/rust

这是 RFC“澄清和简化路径和可见性”的子跟踪问题 (rust-lang/rfcs#2126)
处理crate作为可见性修饰符的问题。

未解决的问题:

  • [ ] 我们如何解析struct Foo(crate ::bar)
A-visibility B-RFC-approved B-RFC-implemented B-unstable C-tracking-issue T-lang

最有用的评论

我个人可以写pub(crate) ,意图似乎很明确。
@johnthagen给出的例子真的很痛苦(使用crate ):

use crate::menu::{Sound, Volume};

crate mod color;

...

/// A type for storing text and an associated color it should
/// be drawn as.
crate struct ColoredText {
    crate color: types::Color,
    crate text: &'static str,
}

crate mod color;尤其令人困惑,您肯定需要对那里发生的事情进行一些思考。

所有91条评论

关于crate作为可见性修饰符的评论:

解析歧义

不自然/混乱/没有改善

一个好主意

pub(extern)改为

自行车棚

早期预览

专用线程

就个人而言,我非常赞成将crate作为可见性修饰符,我在这里分享@stepancheg的评论。 我认为我们应该鼓励更小、更严格的可见性, crate就是这样做的。

我个人可以写pub(crate) ,意图似乎很明确。
@johnthagen给出的例子真的很痛苦(使用crate ):

use crate::menu::{Sound, Volume};

crate mod color;

...

/// A type for storing text and an associated color it should
/// be drawn as.
crate struct ColoredText {
    crate color: types::Color,
    crate text: &'static str,
}

crate mod color;尤其令人困惑,您肯定需要对那里发生的事情进行一些思考。

其中一些示例与 C- static -esque- 直观相关,但非常不同。

@johnthagen 给出的例子对我来说并不好读。 事实上,它读起来很自然,我很喜欢这种对称性。 它在某种程度上是美丽的。

如果可读性:

crate struct ColoredText {
    crate color: types::Color,
    crate text: &'static str,
}

成为问题; 那么理解 Rust 语法的 IDE/编辑器可以用不同的颜色突出显示不同位置的crate标记。 我认为这应该可以很好地消除差异。

crate作为可见性修饰符肯定很奇怪:它使用非常特定于 rust 的关键字来表示不是特定于 rust 的东西。 Kotlin & C# 为此使用internal

我个人希望为crate-visible重用pub $ ,并为世界可见使用更尖叫的语法,例如pub*pub!

以前有人提出过,但我认为我看到的根本问题是:

  1. crate名词。 我认为pub(crate)看起来很长而且很奇怪,所以我完全支持用something替换它,但它确实有与之相关的公共形容词,所以在语法上它流动得更好。
  2. crate现在被用作“this-crate”导入的锚点,这意味着不同于“无论在哪里定义 this,它也会从这个 crate 中明显导出”
// Here `crate` means the root of this crate.
use crate::menu::{Sound, Volume};

// Here, `crate` means: export crate::game::color
// The `crate` is referring to `color`, not the root.
crate mod color;

...

/// A type for storing text and an associated color it should
/// be drawn as.
// Same potential confusion as `crate mod color`
crate struct ColoredText {
    crate color: types::Color,
    crate text: &'static str,
}

与示例相比,使用来自 Kotlin/C# 的@matkladinternal

use crate::menu::{Sound, Volume};

internal mod color;

...

/// A type for storing text and an associated color it should
/// be drawn as.
internal struct ColoredText {
    internal color: types::Color,
    internal text: &'static str,
}

我并不是说internal是正确的关键字(Rust 喜欢它是非常短的缩写,而int不幸的是充满了 C/C++/Java 混淆),但我个人认为第二个示例立即更具可读性。

我还认为crate可见性关键字会让从其他语言来到Rust 的人感到困惑。 如果我们中的很多人都参与了足够多的 Rust 来评论这些线程,我不得不想象它也会让刚接触 Rust 的人绊倒。

pub(crate)稍长的语法如果没有成为警告/错误以使pub项目在板条箱外无法访问,则可能没什么大不了的。 我个人希望,如果我有一个pub(crate) struct Foo { ... } ,编译器可以意识到 $#$ impl Foo #$ 中的所有pub fn s显然都无法访问,并且不会打扰我。

如果我将类型从pub struct Foo标记到pub(crate) struct Foo ,我发现目前在 Rust 2015 中只是忙碌的工作,然后编译器如何在其他pub fn的所有地方大喊大叫pub(crate)类型存在,当问题不存在时,因为另一种类型也是pub(crate)

我还发现@matklad的想法是将pub重新用作 crate-public 并使用export或其他东西进行世界可见的出口。 但这对于一个版本来说可能差异太大了?

pub重新用作 crate-public 并为 world-public 添加新的可见性是当前版本之前的提议。 即使对于一个版本来说,对现有语义的这种改变也被认为过于激烈,这就是为什么pub现在保留其当前含义的原因。

我认为,较少讨论和考虑的是仅通过 lint 重新利用pub 。 也许我们可以将 lint 从“警告在箱子外无法访问的pub ”切换到“警告在箱子外访问的pub ”,并添加一个纯可选pub(extern) / export关键字。 也就是说,不要更改任何语义,只需添加 lint-silencing 语法。

我怀疑这会减少破坏性,基于世界公共项目比 crate-public 项目少的假设。 它还将保留pub的直观(尽管有些不正确)含义作为“从当前模块导出”,而不是让每个人都看到可见性的真实行为。

Rust 喜欢很短的缩写,不幸的是int充满了 C/C++/Java 的混乱

FWIW,虽然它只保存了两个字符,但如果我们想缩写internal ,“正确”的缩写可能类似于extern al,是intern 。 不幸的是,这也是一个具有普遍理解的不同含义的名词。 那好吧。

@glaebhoerl intern是一个不错的选择! ❤️

extern的对称性非常好,IMO 将大大减少与intern的名词形式的潜在混淆。

它很短,(仅比crate多 1 个字符)并且不与use crate::冲突。

更新后的示例如下所示:

use crate::menu::{Sound, Volume};

intern mod color;

...

/// A type for storing text and an associated color it should
/// be drawn as.
intern struct ColoredText {
    intern color: types::Color,
    intern text: &'static str,
}

我之前提到过,但我不确定名词用作形容词有什么问题?

回顾一下:有很多名词可以用作形容词,例如。 一只家猫、一只电脑鼠标、一张电脑桌等……谷歌搜索_英语名词用作形容词_似乎表明没有任何内在错误,尽管并非所有名词都用作形容词。

让我们尝试一下:

_crate mod hello;_ 一个名为 hello 的 crate 模块,感觉不错。
_crate fn world() {}_ 一个名为 world 的 crate 函数,感觉不错。
_crate struct Foo;_ 一个名为 Foo 的 crate struct,感觉很好。
_crate enum Bar {}_ 一个名为 Bar 的 crate 枚举,感觉不错。
_crate trait Baz {}_ 一个名为 Baz 的 crate trait,感觉很好。

_crate use self::local::Foo;_ Ack,这个不行,一个 crate 用吗? 您可以将其读取为名为 Foo 的板条箱可用项目。 它确实打破了模式。

当在结构成员前面使用时,它也可能很尴尬,甚至更多地与 crate 作为路径的根结合使用。

虽然 crate 并不完美,但我不相信“作为名词”是决定性因素。

问题是它非常罕见。 我知道没有一种编程语言使用名词作为类型修饰符。

@Centril

成为问题; 那么理解 Rust 语法的 IDE/编辑器可以用不同的颜色突出显示不同位置的 crate 标记。 我认为这应该可以很好地消除差异。

就个人而言,虽然我发现不同编辑器的功能很好,但我认为我们不应该在设计语言时假设一个足够先进的编辑器。 我觉得 C# 就是这样设计的,这是我对这种语言感到沮丧的一个主要因素。

@epage我认为crate作为可见性修饰符是一个好主意,无论突出显示如何; 我只是建议突出显示是额外的缓解措施。 特别是,对于任何编辑器来说,突出显示crate::crate field $ 不同应该是相当简单的,因为前者总是crate + ::这很容易检查在所有情况下,除了crate ::foo::bar (但这将是相当罕见的......)。

作为一个 IDE 人,我认为这样的突出显示会为很少的信息增加大量的噪音,从而产生负面影响。 IMO(这是高度个人化的,但通过使用和实现强大的 IDE 来了解)突出显示在传达语义非本地信息时效果最好(这种用法是指用mut声明的变量)并且 de 强调代码的本地“样板”方面(因此所有关键字的样式都应完全相同)。

在我看来, dom (即:国内)是一个潜在的候选人。

它有三个字母(便于对齐,易于记忆),它不是名词,而且——除了“文档对象模型”——我认为它没有任何特别的歧义。

pub struct MyStruct {
    dom num: i32,
    pub msg: String,
}

有人对此有想法吗?

我已经看到过但在摘要中找不到的一个角度(感谢您这样做!),即快捷方式如何与现有的pub()语法相匹配。

如果pub<something> (例如crate )具有特殊含义,则它会进一步降低pub(<something>)的可见性,进而降低熟悉度。 无论我们采用哪种解决方案,我认为它应该支持或取代现有的机器,而不是另一种。

例如,如果我们使用crate或替换:

  • 是否应该扩展crate以承担范围限制(例如crate(<something>) )?
  • 我们应该弃用pub()以使pub只有一个含义吗?

考虑到这一点以及我对目标的理解(从内部 API 澄清公共 API),我重新创建了 @vitiralpub(extern)的想法。

  • 适合现有机器
  • imo 通过使pub成为pub(<something>) #$ 的快捷方式而不是特殊情况来改进现有机制
  • 如果公共 API 明显小于私有 API,那么我们就以正确的方式加权了语法。
  • 但是可能会使来自其他语言的人感到困惑,其中public意味着它可能在您的公共 API 中。

RE 对封装的影响

现有pub系统的一个好处是封装。 最简单的方法是只将 API 公开一层。 这使得更容易让东西对 crate 的一部分公开,但对整个创建来说是私有的。

虽然仍然会有pub(super) ,但拥有pub(crate)的快捷方式将推动人们更多地使用它,鼓励人们不要封装他们的 API。

我怀疑这不是问题,因为小板条箱的文化。

但在考虑到这一点时,它给了我对pub(extern)上述评论的另一个迭代

  • pub应该是pub(super)的快捷方式
  • 您的公共 API 需要pub(extern)

我之前提出了人们从其他语言过渡的担忧。 这更好地与他们保持一致。

  • 更接近地匹配public在各种语言中的工作方式
  • 一些语言倾向于对公共 API 有不同的机制,所以这对他们来说是可以解释的。

imo这是世界上最好的。 所以把它拆开,帮助我理解为什么不:)

我仍然讨厌pub(foo)语法。 夸张地说,它看起来无法确定它是函数调用还是多个关键字的混搭。 我们不使用let(mut)for(in)那么这个有什么关系呢?

@parasyte pub<foo>赢了! 毕竟,这不是一种_类型的可见性_?

pub<crate>pub(crate)确实感觉更好。

换营的人的一些想法:

起初我非常反对crate并认为“这正在破坏美好的pub ”。

然后我在我的一些项目中并排尝试了它并让它沉入其中。

坦率地说,几天后我再也无法忍受看着pub(X)了,相比之下感觉很笨重,而且更难阅读。

最初我担心可能存在(视觉)歧义。 但对我来说,相反的事情发生了:如果我现在看到crate ,我知道它是“板条箱的东西”。 无论是导入模块还是声明可见性。 在绝大多数情况下,从上下文中可以清楚地看出究竟是什么(有点像英语中的歧义)。

我可以看到在某些情况下可能仍然存在残留的“更难”(视觉)歧义,但我不想用它来换取现在感觉像是大规模的量化可读性胜利(如:“需要的源代码行更少的视觉标记化/努力与变得更加模棱两可的源代码行”)。

从这个角度看, crate - intern (或任何其他不对称)也会让人感觉像是退了一步。

话虽如此,我不知道解析歧义。 如果我必须选择一个,我宁愿写一个围绕“ crate表示板条箱的东西”的好故事,而不是围绕“ crate ::foo::bar有效”的好故事。

我的两分钱是:

  • 我一直在广泛使用crate modcrate structcrate fn ,...,我发现它非常有用。
  • 我真的不在乎它是如何命名的( cratepub(crate) ,...),只要它不是太长,因为我经常使用它。

如果由我决定,我会使用vis作为关键字,并使用可见性类型作为修饰符,例如vis(pub)vis(crate)等,因为这会产生更多对我有感觉。

鉴于我们已经将pub作为“可见性说明符”,我实际上喜欢cratepub(crate)对我来说是这个模块的公共,对板条箱来说是私有的 - 我发现在这里同时使用公共和私有很奇怪。

在我看来,引入新的关键字和上下文关键字等是不值得的。 教导有两种可见性修改pubcrate ,一个表示公共,另一个表示板条箱私有,对我来说有点道理,但也许我只是习惯了crate已经在过去两周内。

对于那些建议已经使用的关键字(即: crate )混淆含义的人,我认为上下文比单词本身更重要。 大脑使用上下文解析所有内容(编译器如何解析这是另一回事):这解释了为什么我们不将for的语义含义混为一谈for x in yimpl X for Y .

同样,引入crate作为可见性限定符可能不会造成混淆,因为它的含义,在成员或函数限定符的上下文中,在提供附加上下文时是显而易见的。 例如, crate fn my_func();不会读作“这是一个板条箱”,而是读作“这是一个板条箱可见的函数”。

也就是说,它是名词的事实是不一致的。 我将创建一个新的可见性限定符关键字来解决这个问题。

事实上,如果有什么东西肯定会让用户感到困惑,那就是pub(crate)语法,它直观地看起来像一个函数调用,并且在该语言的其他地方没有其他等效的语法。 对我来说,这感觉就像一个丑陋的黑客。

作为对使用crate代替pub(crate)并在阅读@aturon最新帖子后提出担忧的人:

支持 crate 作为可见性修饰符(在此处跟踪 138。鉴于到目前为止的反馈,Rust 2018 不太可能稳定此功能

我只是想确保我很清楚我个人完全赞成用某些东西替换pub(crate) (我认为大多数人都是这样)。

按照优先顺序,我认为最容易掌握的内容,尤其是对于那些刚接触或不熟悉 Rust 的人:

  1. intern (或其他类似的新关键字)
  2. crate
  3. pub(crate)

如果核心团队觉得intern或类似的东西最终永远不会被接受,那么我会落后crate因为我仍然认为它比pub(crate)有了很大的改进,因为@Centril和其他人已经阐明的原因。

因此,如果经验更丰富的核心团队认为这是前进的最佳途径,我不会阻碍这一点。 能够提供反馈表达想法以供考虑是很好的。 👍 生锈!

@ralfbiedert

最初我担心可能存在(视觉)歧义。 但对我来说恰恰相反:如果我现在看到板条箱,我知道它是,嗯,“板条箱的东西”。 无论是导入模块还是声明可见性。 在绝大多数情况下,从上下文中可以清楚地看出究竟是什么(有点像英语中的歧义)。

@zesterer

对于那些建议已经使用的关键字(即:crate)混淆含义的人,我认为上下文比单词本身更重要。 大脑使用上下文解析所有内容(编译器如何解析这是另一回事):这解释了为什么我们不将 for in for x in y 和 impl X for Y 的语义含义混为一谈。

我并不是要亲自将你们两个叫出来,而是作为人们在使用它后支持改变的例子。

虽然我觉得它看起来很奇怪,但我最关心的是非锈动物。

  • 这会如何影响某人在评估生锈时的意见? 一件事语法怪癖不应该足以劝阻他们,但如果你把它与生命周期和任何其他“奇怪”的东西一起堆积起来,它可能会阻止人们
  • 这对人们学习 Rust 有何影响?
  • 这对不了解 Rust 但需要修改它的人有何影响?

如果我们可以对此进行可用性研究以更好地了解这些问题的影响,我会很高兴。

@epage ,非常同意,Rust 面向用户的部分应该进行 UX 测试。 但是,我认为这就是现在正在发生的事情,我们正在讨论结果。

除此之外,我们公司的一些轶事观察:

我是我们部门的“Rust 倡导者”,与其他 3 人一起工作。 所有人都具有扎实的 C# 背景,但对 Rust 来说相对较新。 前几天,我将我们的研究项目连同“ crate -stuff”一起迁移到了 Rust 2018。

当我们浏览代码时,对话大致是这样的:

“所以这是我做的其他一些改变;新的导入系统;修改器。”

“那有什么作用?” (指向use crate::objectcrate x: object

“从这个箱子进口。” 和“可见性修饰符”。

“啊,好吧。还有什么变化吗?”

讨论完毕。

当然,我们聊了更多关于新功能的话题,以及我们是否应该采用某些模式; 但是这两个项目的“教学方面”归结为几句话,此后(据我所知)一直没有被提及。

如果下次我们谈话时我听到任何其他消息,我将更新此评论。

@ralfbiedert感谢分享!

@epage ,非常同意,Rust 面向用户的部分应该进行 UX 测试。 但是,我认为这就是现在正在发生的事情,我们正在讨论结果。

虽然我重视这些 UX 轶事,尤其是在可学习性方面,但我认为我不认为我会将这个问题的过程限定为 UX 测试。 我的评论指的是一个更正式的过程,可以帮助加深理解、消除偏见等。

以下是来自 Rust 新手的一些想法。 首先,我想说的是,这种语言真正让人感觉清醒和美好的是“默认情况下不可变,默认情况下私有”的方法。

现在, pub很好,因为它在现代语言中很简单且符合预期。 我觉得不得不在 Rust 的上下文中取消学习这一点并在各处散布另一个关键字有点笨拙。 从语义上讲,它的意思是“这是一个出现在盒子上的按钮”,盒子就是模块:“从一个级别向上”的可见性。

所以,对我来说,使用crate或其他类似性质的关键字而不是pub只会让人觉得很痒:如果公开是非默认设置,那么从 crate 中导出应该更加特别. 也就是说, crate 的 API 应该以特殊的方式表示。

所以,我完全同意@epage的观点, pub应该保持不变,并引入某种形式的pub(extern) 。 带括号的关键字确实让人觉得毛茸茸的,所以也许它应该有一个专门的关键字。 crate关键字在这个意义上会起作用,我可以看到它的意思是“这是一个导出的板条箱成员”。 或者实际上是export ,我不知道。 也许我的所有观点都是愚蠢的,这一切都相当于“关键字不正确”。 但是pub很常见以至于感觉不到特别,所以它不应该代表真正特别的东西(crate-exported API)。

这个周末我在 RustConf 上观看了一个演讲,在它的代码示例中使用了很多pub(crate) ,这真的让我想要简单的旧crate 。 我还是很赞成原计划的。

@steveklabnik

这个周末我在 RustConf 上观看了一个演讲,在它的代码示例中使用了很多 pub(crate),这真的让我希望使用普通的旧 crate。 我还是很赞成原计划的。

这条评论的上下文主要来自 RustConf 还是它是否考虑到了这个线程并预设了与它的分歧? 早些时候,我提供了一个替代解决方案,不是针对pub(crate) ,而是针对驱动任何pub更改的需求,我希望它能够满足人们的需求。

@superseed

所以,我完全同意@epage的观点,pub 应该保持不变,并引入某种 pub(extern)。 带括号的关键字确实让人觉得毛茸茸的,所以也许它应该有一个专门的关键字。 crate 关键字会在这个意义上起作用,我可以看到它的意思是“这是一个导出的 crate 成员”。 或者实际上是出口,我不知道。 也许我的所有观点都是愚蠢的,这一切都相当于“关键字不正确”。 但是 pub 很常见,以至于感觉不到特别,所以它不应该代表真正特别的东西(crate-exported API)。

RE“虽然带括号的关键字真的感觉毛茸茸的”

虽然我个人认为当我发现它们时它们很整洁(比全有或全无friend好得多),但我更担心的是我们没有创建并行语法,而是接受我们拥有的或找到的替代解决方案。

另一方面...

RE 或者export实际上,

我不认为添加export与我之前的评论相矛盾crate 。 在这种情况下, export可以被视为与可见性不同。 export将暗示pub(crate) 。 我怀疑这在教学中不会有太大问题。

我可以在我最初想法的扩展上采取任何一种方式。

@superseed

pub […] “从上一级”可见。
crate关键字在这个意义上会起作用,我可以看到它的意思是“这是一个导出的 crate 成员”。

我认为您对这两个关键字含义的理解可能与这里提出的完全相反,即pub表示对所有人公开,而crate表示可从同一个 crate 访问。

@epage

pub(crate)的情况下,它确实提供了一个非常简洁的功能,并且实际上读起来很好,但在我看来太像函数调用了。 即没有句法突出显示我可能会很困惑,并且突出显示不应该是理解语言语义所必需的。

@SimonSapin

确实,我意识到这是应该理解的方式,但是crate - 作为一个名词 - 感觉就像它是在声明一个板条箱 (?) 或板条箱的一个属性。 强调声明而不是限定。

public / pub是一个无处不在的限定符,它(对我来说!)感觉不应该意味着“这是从板条箱中导出的”。 它的含义是“这在我所处的上下文之外是可见的”,就像它用于限定struct成员可见性的情况一样(如果我错了,请纠正我,但我没有t认为在这种情况下语义正在改变)。

而 public/pub 是一个无处不在的限定词,它并不觉得(对我来说!)它应该意味着“这是从板条箱中导出的”。 它的含义是“这在我所处的上下文之外是可见的”,就像它在限定结构成员可见性中的使用一样(如果我错了,请纠正我,但我不认为语义是在这种情况下改变)。

pub一直意味着“这是从板条箱中导出的”——这不是变化,它已经是这样了。 这么多人假设的事实是pub(crate)可见性级别被推高的原因。

pub 一直意味着“这是从板条箱中导出的”

我认为这种理解也可能会引起一些混乱,因为这不是全貌。 pub真正的意思是“我不在乎这个模块之外的谁访问这个项目/字段/方法”。 要从 crate 中导出,它仍然需要该项目的路径也具有相同的pub修饰符。

这个细节在许多语言中都很常见。 这也是我不热衷于unreachable_pub lint 的原因,因为这是推动这个问题的部分原因。 如果我有一个从未在顶层导出的类型,那么麻烦我说我标记了它的方法pub感觉就像噪音。

@rpjohnst真的是它一直以来的意思吗? 是不是从箱子顶部的“从super可见”链导出了一个元素,并且没有将叶子元素本身限定为pub

不,这不是全部, @seanmonstar的澄清暗示了其余部分。 最大的例外是再导出——你可以pub use一些父模块是私有的。 一个更奇怪的例子是这样的,即使它的父模块是私有的,你也可以在公共接口中使用pub项。

而在另一个方向上, pub(crate)和较小的可见性不能以同样的方式被绕过 - 你不能pub use还没有 $#$ pub $#$ 的东西,即使pub use从箱子外面看不到。

因此,一个项目自身的可见性并不直接与它对super的可见性有关,而是它在任何地方的可见性“上限”。

哦,好的,谢谢你的澄清! 我有一个更天真的模型。 对于之前关于“pub 的当前含义”的评论,它更有意义。

我们今天在@rust-lang/lang 会议上简要讨论了:

  • 我们中的许多人对此感到乐观,但对关键字的选择以及与crate::foo::bar路径结合是否会造成混淆仍有疑问
  • 然而,值得指出的是,我们确实必须决定以何种方式来解析struct Foo ( crate :: foo :: Bar ) —— 这是(crate::foo::Bar)类型的私有字段还是crate字段::foo::Bar类型的?

    • 老实说,我不确定我们今天解析的是什么

答:我们将其解析为路径(游乐场)。

这似乎..对我来说可能没问题,因为我认为::foo::bar路径将变得越来越少。

我们中的许多人对此感到乐观,但对关键字的选择以及与 crate::foo::bar 路径结合是否会造成混淆仍有疑问

@nikomatsakis是否有会议记录或摘要供我们跟进? 在这个线程中,我至少没有看到讨论过我的一个担忧[0],也没有看到太多关于反提案的讨论。 也许 [0] 和其他一些在各种内部线程中进行了讨论,但这需要深入研究。

[0] 创建一种并行可见性语法,将pub(...)推向默默无闻的状态,感觉我们应该删除或接受pub(...)

@epage

@nikomatsakis是否有会议记录或摘要供我们跟进?

不抱歉; 我们没有讨论很长时间(最多几分钟),也没有写下任何关于它的会议记录。

@eddyb简要提到my是一种更短且更符合人体工程学的可见性修饰符。

我想我说的是minemy更短,可爱!
(为了记录,我在会议上半开玩笑)

编辑:如果my是本地箱,我们可以用 our 替换pub our吗? 例如:

our struct Foo(my FooImpl);

(为了记录,我在会议上半开玩笑)
如果my是 crate-local,我们可以用 our 替换pub our吗?

Perl:让笑话成为现实。
https://perldoc.perl.org/functions/my.html
https://perldoc.perl.org/functions/our.html

my很好(并且以前出现过),问题是它并不比localinternal或其他任何东西,关于什么(或在它的情况下,谁的),这是整个问题。

我们所处的尴尬情况是我们想要拥有三个隐私级别——“完全公开”、“完全私有”和“介于两者之间”(即 crate 级别)——并且由于向后兼容性限制,我们'坚持第一个必然是pub ,第二个是隐含的默认值,并且必须为第三个想出一些新的东西。 并且英语中没有很多词可以精确地表示“既不是完全全球性的,也不是完全本地化的,而是介于两者之间的某个地方”。

crate关键字确实满足了这一点,因为它在名称中正确说明了实际范围是什么——它是板条箱。 但是(在你说出“我知道!”之前),这样做的代价是它不再是一个可见性修饰符。 “Pub”是“public”的缩写,即可以直觉。 但是没有其他语言具有“板条箱”(具有该名称)的概念; 要理解crate struct的意义,首先必须了解这一点。 1它需要我们进一步退出“语言陌生度预算”,对于余额是否仍然是正数,可能会有不同的看法。

同时pub(crate)解决了这两个问题——它告诉你它是一个可见性修饰符,它告诉你范围是什么——但作为交换它又长又尴尬。

所以这基本上是困境的形状。

1 (上面有人描述了一个交互,“他们问crate是什么意思,我告诉他们这是一个可见性修饰符,这就是它的结尾”。有问题的,可能更常见的情况是,当你不会碰巧有一个 Rustacean 坐在你旁边。)

FWIW,对于板条箱本地物品,我完全可以接受ourmy
our甚至是一个三字母关键字,并且与pub ~ 和 Perl~ 很好地对齐。
问题是他们听起来太不正式(对于以英语为母语的人?)?

人们如何看待intern ? 它比crate长一个字符,但除此之外,我认为对于 Rust 新手来说它比crate更直观,并且与extern关键字有一些很好的对称性。

IMO ourmylocalinternal $ 具有相同的弱点:它们不太清楚自己的范围。 local尤其令人困惑,因为范围与局部变量完全不同,其中 local 意味着封闭范围的私有,对于一个项目来说,它是模块,而不是板条箱。 我发现internal有点不具体。 对内是什么? 模块? 方式? 箱子? 对于那些来自使用它的语言的人来说,这可能是显而易见的,但对于其他人来说则不一定。 ourmy更加模糊。 相比之下, crate的作用域就很清楚了。

关于pub(extern) ,我实际上有一个问题。 extern "C" fn foo() {}没有前面的pub是否有意义? 因为如果没有,我们也可以将extern fn foo() {}重用于我们常规的、非“C”的 abi Rust 函数。 我在想我们可以统一这一点,而不是为 FFI 保留 extern 语法。 这意味着extern现在去糖为pub(extern)pub与今天保持相同,但在项目支持时接受可选的 ABI 字符串,以及pub的 lint pub(extern)extern的情况下被导出。

extern fn foo() {
    println!("Just called a Rust function from Rust!");
}

#[no_mangle]
extern "C" fn foo_from_c() {
    println!("Just called a Rust function from C!");
}

如果之前讨论过这个问题,我还没有看到我读过的线程中提到的非常抱歉!

如果没有前面的酒吧,拥有extern "C" fn foo() {}是否有意义?

是的 - 例如,有时您只想将foo用作函数指针。 事实上,语法extern fn foo() {}无论如何都不能被重用,因为没有"C" extern为 C ABI,这至少被某些人认为是惯用的。

这是很久以前提出的一个建议——也许我们可以再给它一次机会,因为新模块系统的某些方面已经具体化了?

// Public to the world.
pub struct Foo;

// Private to the crate.
priv struct Foo;

// Basically not visible at all (only inside the module).
struct Foo;

如果有人想到:我相信这是有道理的:

  • “公开”为“对世界公开”
  • "private" 为 "private to the crate"
  • “没有可见性修饰符”为“基本上根本不可见”

有些人对priv不是最严格的限制有下意识的反应,但我想就此提出两点:

  1. 在 Rust 中,模块用于在命名空间下组织事物,而 crate 用于定义接口。 因此,如果 crate 是“API 单元”,那么可见性修饰符主要讨论 crate 是有意义的。

  2. 我认为Java犯了一个错误, private的意思是“类私有”,而没有可见性修饰符的意思是“在包内可见”。 对我来说,没有修饰符(即默认值)成为最严格的更有意义。

@stjepang我会争辩说,在大多数其他情况下,“私有”将比未修改的状态具有更严格的含义。 一般意义上的私有默认公共频谱类似于受保护的可用广告。

私人俱乐部比俱乐部更独特。
私人行为意味着采取了一些努力来躲避公众的视线。
鉴于进化未能赋予我们心灵感应,私人思想应该是一个多余的概念,但大多数人认为它意味着一种不打算分享的思想。

作为一个没有经验的语言学习者,我还建议与具有多个上下文相关含义的单个关键字相比,一个额外的关键字对认知负担的影响更小。 参见 Costello, L 和 Abbot, B “谁先上”,1938 年。

我觉得myour会有我们不想要的内涵,抛开保留这些作为关键字的困难。 他们开了一个有趣的笑话,但我认为我们不应该走那条路。

老实说,我觉得人们会了解crate可见性的含义。 我认为更大的问题来自难以阅读或难以解析的代码:

crate struct S(crate crate::Foo);

crate struct S(crate ::Foo);

我个人并不认为这些是阻碍因素,但在这里它们绝对是合理的担忧。

在 Scala 语言中有一个pub(path)的并行,即private[path] ,其作用几乎相同。 它的读法略有不同,说“这个项目是私有的,只允许 $path 内的人查看它”。 但是在 Scala 中将某些东西设为私有需要一个注解,因为默认值是公共的,而 Rust 不是这种情况。

我突然想到friend的 C++ 概念也类似于pub(path) ,这是另一个先例。

最终,问题是:

  1. crate关键字用于相对路径导入和可见性修饰符,
  2. crate关键字阻止了统一的可见性修饰符语法,例如pub(...)
  3. 有些人不喜欢pub(crate)语法(太长,看起来像函数调用)。

经过 5 分钟的深入思考后,我想出了以下内容……:P

可见性修改器的特殊语法

_(以@为例)_

<strong i="18">@pub</strong> use crate::Foo;

<strong i="19">@crate</strong> struct Bar;

我个人觉得这很丑,我不想输入@cratepub(crate)

不同的可见性修饰符关键字

由于已经有pubextern ,我认为crate关键字非常适合(来自具有通常publicprotected的语言时不足为奇private关键字)。

crate struct Foo;

crate fn path() -> PathBuf { ... }

但正如我所说,这不适用于crate导入前缀,我开始觉得我们倒退了。 我觉得真正的问题在于路径清晰度的变化,例如,没有语法突出显示:

use crate::utils;

看起来crate没有任何特殊含义。

从声明性宏语法中获得了很大的启发,而不是为可见性修饰符找到某种统一的语法,我宁愿拥有以下内容:

use std::io;
use std::path::Path;

use log::info;

use $crate::utils;

crate fn hello() -> io::Result<()> {
    utils::rm_rf(Path::new("/"))?;
    info!("Goodbye, World!");
}

( self , supercrate路径导入的特殊锚点需要一个前缀,例如$crate ,这很清楚它们是特殊的并且看起来像变量)

复杂的例子:

crate struct Foo(crate crate::Bar);

变成:

crate struct Foo(crate $crate::Bar);

似乎存在重复的跟踪问题: https://github.com/rust-lang/rust/issues/45388。
关闭那个有利于这个。

rustc 实现对这个特性进行 dogfooding 是有原因的吗? crate fn的每个实例都可以更改为pub(crate) fn并停止依赖不稳定的功能 AFAICT。

我看不出我们这样做的充分理由。 Clippy 和 rustc 都一直使用不稳定的特性,它们应该这样做,因为它允许我们在捕获实现中的错误方面更广泛地测试它们,并且因为我们可以了解如何使用它们。

在这种情况下,在 Clippy 和 rustc 中使用crate作为可见性修饰符在我看来表明它比pub(crate)更好,并且此跟踪问题中引用的大多数问题都不是——实践中的问题。 我认为https://github.com/rust-lang/rust/issues/53120#issuecomment -413466129、 https ://github.com/rust-lang/rust/issues/53120#issuecomment -414392549 和https:// /github.com/rust-lang/rust/issues/53120#issuecomment -413498376 还建议crate作为可见性修饰符在外面也能很好地工作。

由于它在实践中运行良好,因为我们语言团队中的许多人对此感到积极,并且因为它被 RFC 接受,所以我认为在考虑如何解析struct Foo ( crate :: foo :: Bar )之后,我们应该考虑稳定crate作为可见性修饰符struct Foo ( crate :: foo :: Bar )目前作为路径,可能是对的)。

FWIW,我在cargo-n64中使用了这个功能,它非常好!

我一直在 rustc 中使用crate ,因为它很短并且没有像pub(crate)这样的括号。
我仍然不喜欢它的阅读方式。
它也没有 3 个字母,因此格式会随着pub <-> crate的变化而变化。
我还是很喜欢our

@petrochenkov我对您所写内容的理解是,您更喜欢crate而不是pub(crate)但也想要比crate更好的东西。 这是一个准确的评估吗?

FWIW,我仍然认为intern是一个不错的选择。

  • 没有括号
  • pub(crate)短(虽然比crate长一个字符)
  • crate更好读(IMO)(用于路径等其他内容,例如crate::foo::bar
  • 与现有的extern有很好的对称性
  • 熟悉,因为其他语言使用类似的东西( internal )。 Kotlin、C# 等

话虽如此,我也同意pub(crate)应该替换为something

那么它就解决了——三个字母很好,“实习生”很好,所以...... int 。 ;)

我们可以为crate ::T (带空格)的情况提供一个 lint 吗?

@Centril我们中的一些人只是在#rocket 上讨论前进的方向。 根据您最近的评论,是否有可能在不久的将来提出 FCP?

@jhpratt我一直想写一篇文章并最终提出,但我一直忙于其他事情。 我会尝试在不久的将来找到一些时间。

所以,我只是在浏览问题,寻找有关宏的东西,然后找到了这个。

现在我们pub(crate)已经稳定了很长时间了,我想知道,这个问题不应该关闭吗?

有趣的是,这只是在最近的一次 lang-team 会议上提出的,在那里我们讨论了这个问题,我们希望重新讨论这个问题。

就个人而言,我绝对怀念能够在字段上写crate fncrate foo: T之类的东西。 这与pub(crate)的语法差异并不大,但我发现它使代码更具可读性,尤其是在具有大量公共字段的结构中。 我还发现它有助于建立一个合理有用、简化的“隐私模型”——

  • 结构,字段要么是模块本地的(非常狭窄的推理)
  • 或者它们在当前板条箱中的某个地方使用( crate ,必须到处乱跑)
  • 或者它们对世界公开( pub

在我看来,这个关键字与https://github.com/rust-lang/rust/issues/48054中设想的更改之间存在一些交叉点,我更愿意确保我们一起采用它们。 我忘记了细节,但我记得在尝试将上述模型付诸实践时会出现错误。

我认为迈向这一目标的第一步是让某人尝试写一篇记录历史的文章,并确保我们已经强调了提出的所有问题。

我确实记得的一个具体问题是

struct Foo(crate ::x)

今天,这是被接受的,并且crate ::x被解析为路径crate::x ,但似乎用户的意思是crate作为可见性修饰符,而::x作为路径.

我倾向于重新引入crate修饰符并保持对上述案例的解析与今天一样。 自 Rust 2018 以来, ::foo路径已在很大程度上被弃用——它们仍然存在并且在某些特定上下文中可能有用,例如宏,但我们现在鼓励使用看起来像crate_name::b的绝对路径的大多数术语crate_name可能是关键字crate或其他一些 crate 的名称。 所以很可能crate::x (忽略空格)实际上一个路径,因此当前的解析是正确的。

如果我们想解决潜在的用户困惑,那么我认为对空格敏感的 lint 是一个相当不错的主意。 换句话说, struct Foo(crate ::x)会发出警告,但struct Foo(crate::x) $ 不会发出警告,尽管它们都被接受且等价。

就个人而言,我更喜欢统一的语法和简单的lookahead(1)解析器; 此外,一个板条箱就是一个板条箱,那里有crates.io ,所有这些东西都与可见性_directly_ 无关——仅在pub(_)的上下文中。

但是,好吧,我知道什么? 这只是另一个观点。 你们无疑有更多的经验和更有价值的意见。

@nikomatsakis好奇您是否对此用例的intern有任何想法(或者此时是否保留了超出范围的新关键字?)。

几年后,我的感觉还是一样:优化可读性。 我发现我代码比代码要多得多。 因此,我的观点是,阅读pub(scope)对我来说更清楚,它可以是pubpub(crate)pub(super)pub(in proto::h1) .


但是,我认为我们并没有真正在与这些意见的对话中添加任何内容,我相信我们已经在之前的评论中说了这一切。 该决定是否应该基于新事物? 或者换一种说法,我们如何决定现在采用这种语法的决定应该是肯定的,而几年前是否定的或推迟的?

我还发现它有助于建立一个合理有用、简化的“隐私模型”——

  • 结构,字段要么是模块本地的(非常狭窄的推理)
  • 或者它们在当前板条箱中的某个地方使用( crate ,必须到处乱跑)
  • 或者它们对世界公开( pub

我无法理解这种简化的好处以及表达能力的损失如何值得。 对于它的价值,我经常将项目公开给它们的父模块(以便兄弟模块可以使用它们),而不是整个板条箱。 对祖父模块的公开较少,但仍然偶尔。

我觉得弃用pub(super)将是一个重大损失。


另外,我不喜欢使用名词crate作为修饰可以独立存在的项目的限定词。 为了比较:pub(lic)、unsafe 和 const(ant) 是形容词。 已在项目定义中用作关键字的名词不是限定词,而是表明该项目的性质:功能、特征、模块……

板条箱已经是我们处理的一个概念,但是在这个提案中,以crate开头的项目定义并没有定义新的板条箱。

一点点新信息:

rust-analyzer 提供了添加pub(crate)的补全和辅助功能,这(主观上)在三个击键时减少了打字的烦人。

我同意这里添加的新信息很少。 我认为一个好的下一步是,如果我们想要支持该提案,将返回,总结突出的问题,并将其提交给 lang 团队尝试做出最终决定。 我当然更愿意接受或拒绝这个提议,我厌倦了让它悬而未决。

通过快速浏览整个问题,我认为@nikomatsakis最近的评论总结得很好。 除了少数人仍然支持intern之类的东西之外, crate似乎是最合乎逻辑的(并且在RFC 2126中被接受)。 唯一真正突出的问题是如何解析fn foo(crate ::bar)

默认情况下对空格敏感的 lint 似乎是最合乎逻辑的起点。 crate ::bar当前被解析为路径,并且在 Rust 2015 和 2018 中都必须保持这种状态。但是,我认为它将成为一个版本中重大更改的候选者。

crate ::bar案例在实践中完全不重要,并且已经与语言的其余部分保持一致。 它一直是讨论的红鲱鱼,请不要关注它。

我当然更愿意接受或拒绝这个提议,我厌倦了让它悬而未决。

我想我会投反对票。
我一直在 rustc 中使用crate而不是pub(crate) ,尽管它更短,但大多数时候它仍然看起来不合适,尤其是在字段或导入上,并且在crate之间进行选择pub(crate)感觉就像在两种邪恶之间进行选择,但不确定哪个是较小的。

是的。 更重要的是, @SimonSapin提到crate bar确实没有定义新的板条箱,尽管它读起来像它。

@petrochenkov您认为棉绒没有意义吗? 就个人而言,如果我看到crate ::bar而没有看到这个讨论,我希望它的行为类似于pub(crate) ::bar 。 我认为在路径中允许空格_at all_是令人困惑的。

@jhpratt由于解析器如何使用标记的性质,很难否认路径段之间的空格。 此外,它还会破坏大量的工具,如 quote!、syn 和许多其他工具。

我想我也会支持关闭它。 我已经看到对这个结构的含义表达了足够多的混淆,我认为简洁的好处不值得潜在的可读性损失。

这可能更适合“更大”(但已关闭)跟踪问题 #44660,但它也与可见性修饰符直接相关,不适合 #44660 的其他子问题:

在某些时候,我记得有人建议pub(path)现在可能是合法的,替换pub(in path)形式并包含pub(crate)pub(super) 。 这似乎是对pub(...)的一个很好的简化,尽管方向与crate不同,我们也可以考虑。

编辑:实际上不确定这是否有效...... struct S(pub(path) Type)struct S(pub (Type,))之类的东西模棱两可(对于LL-esque解析)。

所以在过去的几个月里,我一直在思考这个问题。 我想我已经走到了“接近”的位置。 我想我的立场总结如下:

  • “三个可见性级别”(模块、板条箱、世界)的想法相对简单,很吸引人,但它确实错过了常见的现实世界案例。 特别是pub(super) ,我发现它在实践中非常普遍,尽管很少需要超过一个级别(即pub(crate::foo) )。 例如,我经常希望拥有具有子模块的模块,并且对这些细节使用 crate 可见性无法传达预期的隐私级别。
  • “本地到 crate 的某些部分,或对世界公开”的模型在概念上也很简单和优雅,它涵盖了所有这些用例。
  • pub(crate) fn虽然比crate fn简洁得多,但还不错。 很可爱pub(crate)扩展到pub(crate::foo) ,它涵盖了我有时会得到的另一个用例(即,板条箱中的“大”模块),但这太冗长了,我怀疑它很少会发生被使用,而且很可能这些大模块无论如何都会更好地分解到子箱中......
  • 我认为围绕“混合隐私级别”的最大人体工程学障碍来自与我们试图确保(例如)出现在pub(x) fn中的所有类型都具有适当隐私的 lints 和错误。 我们已经调整了这些规则,我只是觉得我没有再遇到这些烦恼了——如果出现更多这样的烦恼,我认为我们也可以解决它们。

    • 例如,可以在字段上写pub来表示“与结构本身一样公开”,我认为这很好。

当然,现在不添加crate fn并不意味着我们以后不能添加它。 特别是,我认为如果我们有办法在另一个 crate 中做“轻量级、内联 crates”,“三级隐私”模型会更好。 即,如果不是在箱子中包含子模块,我可以声明一个私有箱子,其中包含所有暗示(最值得注意的是,可能是与其他事物的类似 DAG 的关系)。 我不确定轻量级的内联板条箱是否真的可以工作,但它们可能会覆盖和替换很多类似pub(super)的用例。 如果曾经对此进行探索,那么我会考虑围绕crate fn重新展开讨论,因为它可能会变得更加有用/普遍。

简而言之,我赞成从编译器中删除crate可见性级别并删除功能门。

我只是想检查一下这个功能什么时候可以稳定下来,并对有计划删除它感到惊讶和失望。 自从它被添加后不久,我就一直在日常使用它,而且我几乎不受上面讨论的任何问题的困扰。 例如,我还没有写任何类似于crate ::path的东西,而且可能永远不会写,因为我从来没有接触过::path语法。

当然,还有改进的余地。 可以更好地选择关键字, pub(super)仍然不方便,而且当我对辅助方法使用 crate 级别的可见性时,到处都会出现死代码警告,这很烦人。 但是,作为用户,我更愿意保留此功能(功能门控),直到找到更好的解决方案。 pub(crate)语法有点碍眼,有点不鼓励将大模块分解成更小、更紧凑的模块。

认为围绕“混合隐私级别”的最大人体工程学障碍来自与我们试图确保(例如)出现在 pub(x) fn 中的所有类型都具有适当隐私相关的 lints 和错误。

这是否意味着我们也不启用“无法访问的 pub”lint ( mod private { pub fn f() {} } )?

嘿,当我阅读各种 rustc 代码并看到crate正在使用时,我一直在重新考虑,我想这些案例都是pub(crate) ......可能值得做实验性的过渡只是为了看看差异让我感觉如何。 我记得当我们把它从夜间搬到马厩时,我为粉笔感到难过,尽管我认为现在我已经习惯了它并没有那么困扰我。

@matklad我希望不是,我认为 lint 也很重要。 老实说,我不完全确定为什么我没有遇到过我以前遇到的任何类型的错误和烦恼。 也许我最近写的 Rust 代码不够多! 我绝对记得,我曾经有过这些烦人的循环,似乎我无法满足编译器的要求,除非通过制作比我想要的更多的东西pub

保留pub(crate)并添加别名pubc怎么样? 这读起来非常相似,不会破坏任何当前代码,并且不需要键入括号(这使它更快一点)。
这也将允许pubs在父级中可见。

简而言之,我赞成从编译器中删除crate可见性级别并删除功能门。

当我阅读代码时,我发现pub(crate)有点碍眼和嘈杂。 使用crate可见性修饰符会非常好。

当然,现在不添加crate fn并不意味着我们以后不能添加它。 特别是,我认为如果我们有办法在另一个 crate 中做“轻量级、内联 crates”,“三级隐私”模型会更好。 即,如果不是在 crate 中包含子模块,我可以声明一个私有的 _crate_ ,其中包含所有暗示(最值得注意的是,可能是与其他事物的类似 DAG 的关系)。 我不确定轻量级的内联板条箱是否真的可以工作,但它们可能会覆盖和替换很多类似pub(super)的用例。 如果曾经对此进行探索,那么我会考虑围绕crate fn重新展开讨论,因为它可能会变得更加有用/普遍。

我肯定会喜欢你提到的这些“轻量级”板条箱! 这比立即去工作区要好得多,因为工作区有点重。

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