嗨!这里是娱乐寡姐,为你带来最新娱乐资讯。金鸡奖给周冬雨于适的海报P了衣服,可以说是毫无违和感,金鸡奖制作方这是怕天气凉了他们冷是吧!胳膊都不能露了,笑死我了,这么大一个奖怎么还封建呢!
周冬雨的黑白礼服也不性感啊,为什么要P。干净利落充满了女强人气质,结果将露的地方都P上了,质感下降一大截,看起来变成买保险、买房的职场女性。于适虽然穿的是真空西装,但是一点也不娘,充满了阳光正气。结果给他P了一个内搭,让他整个人臃肿了不少。
不少网友表示:“天气冷了,下次别费劲了,都穿羽绒服吧”、“有必要吗?他俩图片那么多,直接换一张不漏肤的照片不就行了”、“下次直接让上传一寸免冠照片不妥了,省力省时省事”等等。
以上就是娱乐寡姐带来的相关内容,有兴趣的关注一下吧,爱你!
这部分很可悲:
> ……并说应该不惜一切代价避免在基础版中添加 Rust。
我对 FreeBSD 的构建系统一点也不熟悉,所以不知道集成 Rust 有什么困难。但是,Linux 可以做到这一点,是的,所有的 “酷小孩 ”都在这么做,但原因是正确的。Rust 的确是为低级代码设计的高级编程语言的极佳组合。
我希望他们改变主意。考虑更多的 C++ 而不是 Rust?唉
Linux 可以做到,但 Rust 不在 linux 内核源代码树中。FreeBSD 的理念是,构建基本系统所需的一切都在 /src(过去包括 gcc,现在包括 llvm),因此如果 rust 工具(即使是用户空间工具)包含在 /src,那么 rust 工具链也必须包含在 /src。但当你需要更新或更强大的 Rust 时,这就会带来问题(FreeBSD 以前在基本系统中使用 perl 时就遇到过这个问题)。有些人建议 rust 可以留在 ports 中, 而需要 rust 的代码可以合并到 base 中。这将是对过去的一种突破。另一些人(PHK)则认为他们应该努力缩小 src 的规模,并尽可能地将其转移到软件包中。这就是 Linux 的工作方式,而且我认为这已被证明是个好主意,但也会有阻力。
src “是可以启动的,还是仍然需要现有的工具链才能启动/src-hosted工具链?
根据我多年前的记忆,“make buildworld ”使用现有的工具链引导新的工具链(即构建,然后用构建的版本重建),然后使用新的工具链构建其他一切。
想象一下,你的基于 Linux 的操作系统中包含的 C 编译器是由 Linux 内核开发人员用来编译系统中内核版本的 C 编译器决定的。他们使用的 Rust 编译器也是如此。这与 FreeBSD 的情况非常接近。这是要付出高昂代价的。
如果他们是这样做的,那么添加 Rust 确实或多或少是无用的。
> 看一下 rustc 兼容性图:如果你看一下流行的 crate(被积极维护和使用的),那么一半以上需要 Rust 1.76 以上。Rust 1.76 是今年 2 月发布的。今年,是的。
其中有超过一半需要 rustc 1.76+ *才能构建它们的最新版本!* 把这些 crate 往后推几个版本,你就会看到截然不同的景象。
我也发现 Rust 文化中 “使用每个 crate 的最新版本,否则就完蛋 ”的心态很有问题,但我在 Debian 稳定版上只使用 Debian 稳定版提供的 Rust crate(和 rustc)就成功地完成了 Rust 开发。
为什么呢?我认为,LTS 方法实际上是错误的。理论上,你可以获得稳定性和更少的错误。在实践中,我使用过 Debian 稳定版、Ubuntu LTS 和 Arch Linux,Arch 是其中最稳定的(我遇到的少数几个错误都得到了及时修复)。
如果你使用的是 Ubuntu,但由于后退修复而出现故障,那么在下一个 LTS 出现之前,你就没戏了(除非你有商业支持合同,也许吧?)
在笔记本电脑的挂起/恢复工作和不出错方面,Arch Linux 也是最稳定的。
这向我表明,LTS/稳定版在实践中并不奏效。它们的测试结果不如上游版本完善,因此漏洞也更多。现在,我并不是说你应该运行最先进的版本(你应该运行 debian 测试版,而不是不稳定版)。如果你有一些关键性的东西需要依赖这些东西才能运行,那么你就应该在部署前进行自动化测试。事实上,无论如何你都应该进行自动化测试,否则在从一个稳定版更新到下一个稳定版时,你就会冒着被破坏的风险。
实际上,我认为继续使用旧版本的唯一理由(除了在特定错误修复之前的临时性)是,如果你在法律上需要认证(例如,医疗设备、汽车等软件的认证编译器)。在这一点上,你最好也有非常好的自动化测试。(并在编码等方面遵循一系列相关标准)。我的日常工作是开发安全关键型系统,这涉及到很多方面)。
我想我们偏离了真正的主题(最流行的 crate 需要什么版本的 rustc),但这个话题也很有趣,所以我就笼统地回答一下吧:
我从事研究工作。为此我需要电脑。我确实*喜欢*维护计算机系统(例如,这就是我作为 DD 参与 Debian 项目的原因)。不过,我年纪越大,维护电脑的时间就越少。因此,为了能够完成我的工作,我需要我的设备在我开始一天的工作时是*可预测的。最新、最棒的东西会不会坏其实并不重要(年轻时的我在这样的生活中肯定没怎么遇到过坏东西),重要的是东西会不会从我脚下变走。我能否从昨天离开的地方重新开始,还是需要适应已经改变的世界?
每隔两年左右,世界就会彻底改变,我觉得这样的节奏刚刚好。一年一个周期也可以。重点是,我已经开始喜欢并欣赏传统的、缓慢迭代的发行版(以及它们发布的所有软件)的稳定性(*)。我在这里使用的 “稳定 ”一词非常具有 Debian 风格:不是指不会崩溃的稳定,而是指不会改变(无论好坏)的稳定。
所以,我想这让我成为了一个怪人,既喜欢城里最时髦的编程语言,又*喜欢经典发行版的老办法。(我甚至还是一个很希望在 Rust 中看到类似 ABI 稳定性和动态链接的怪人,但这当然会面临很多技术障碍)。
我补充一下: 当然,人们有时需要获得最新、最棒的东西。尤其是在研究领域。虽然我完全不接受 “将一切容器化 ”的时尚,但我非常欣赏容器系统,它能让我在稳定的基础系统之上,在研究项目依赖于昨天发布的版本的神经网络或诸如此类的东西时,启动独立的边缘环境。这是世界上最好的解决方案。
> 每隔两年左右,世界就会彻底改变,这对我来说是最合适的节奏。一年一个周期也可以。
>> 世界每两年左右彻底毁灭一次,对我来说是个合适的节奏。一年一个周期也可以。
> 但你确定这是因为老年人就是这么想的,还是因为这是唯一的选择?
是的,我就是我自己轶事中的 “老年人”。我不是很老,但比十年前要老,那时我非常喜欢拥有所有东西的最新版本。我不能确定我态度的转变是否源于年龄。但我想这并不重要,重要的是我的态度发生了实实在在的变化。
我不是很老。
> 我不是很老,但比十年前要老,那时我非常喜欢拥有所有东西的最新版本。
> > 我在这里使用的 “稳定 ”一词非常具有 Debian 的风格:不是 “不崩溃 ”的 “稳定”,而是 “不改变(无论好坏)”的 “稳定”。
> 是的。这正是长老们强烈要求的,但他们实际需要的很可能是 “不崩溃 ”中的 “稳定”。
为什么不能两者都重要呢?重要的是稳定,就像 “如果昨天还能用,今天就必须以同样的方式工作”。
是的,我每天都生活在这样的环境中!
作为一个需要赡养残疾妻子和年迈父母的人,我最大的问题就是变化。这就是为什么我们直接从 XP 升级到 Windows 8.1。我的妻子几乎没有碰过 7 或 8.0,因为我*无法面对升级给她带来的痛苦,直到她的 XP 电脑因为老化而几乎无法使用……
干杯
沃尔
你在升级时有多痛苦?
至于两者兼得,那就更好了。不过在实践中似乎不会发生。
问题是你不知道升级会有多痛苦,而回滚通常又不可行。
我在升级前会对系统进行磁盘镜像,但大多数人做不到。
我的时间不多,没钱花两天时间去追查为什么我更新的 GPU 驱动程序会导致我使用的主要应用程序在所有东西上叠加模糊方块。如果它能正常工作,我就会尽量减少更改,直到有应急时间为止。由于家用台式机和笔记本电脑系统实际上都是独一无二的(hw+sw+配置组合),因此总是有可能出现混乱。
例如,自从上次更新 Ubuntu 之后,我就遇到了一些令人沮丧的问题,火狐浏览器和基于 Electron 的应用程序都出现了周期性的显示故障。现在没时间深入研究,只能忍着。上次更新后,我的笔记本电脑显示服务器在拔掉外接显示器时开始冻结。在此之前,它还会周期性地出现无法挂起或恢复的问题……而这是在一个广泛使用的发行版上得到很好支持的硬件。不可否认,nVidia GPU 是问题的一个重要原因,我通常能避免这个问题,但这台机型却无法避免。
我必须维护的 Windows 系统甚至更糟,因为它们会强制进行重大更新。它们很少被使用,所以当我每隔几个月启动它们时,它们往往会被立即强制更新禁用……
> 不过,一旦你真的升级了,会有多痛苦?
相当痛苦。不过,我觉得没有 7 或 8.0 那么痛苦。正是因为 8.1 恢复了很多 XP 的外观和感觉。
不过,现在已经退化成了 “支持任何东西都是痛苦的”,因为岳父岳母已经无法完全适应技术,而我的妻子也一直在挣扎。这就是为什么现在改变是如此痛苦–如果事情没有改变,至少她还有机会记住该怎么做,如果改变了–就没有机会了!
干杯
沃尔
很有道理,我宁愿用一台每次都能从挂起状态正确唤醒到内存状态的笔记本电脑(在 Ubuntu 或 Debian 上从未见过这种可靠的工作方式,而在 Arch 上则有 99.9% 的可靠度)。我想这完全取决于你关心哪些稳定性指标。
即使在 Arch 上,大多数更新也不会 “颠覆世界”。是的,有时会这样(KDE 6),但这种情况很少见,因为上游并不经常发布此类重大版本。我想问题在于,你是想要每年发布 3-4 次更新(而且只是针对特定子系统的部分小更新),还是想要积累一大堆更新,然后一次性全部发布。
说真的,除了 KDE 6 之外,我想不出去年有什么重大更新。是的,有两三次更新后,我也遇到了一些小 bug(很快就报告了,也很快修复了,而且都不是什么大问题),还有一次我遇到了一个相当恼人的 bug(登录管理器中的蓝牙默认为关闭,一旦我弄明白了,很容易在配置文件中修复,而且在笔记本电脑上,这只是一个不方便的问题,而不是什么大问题)。
在我工作的地方,我们从 LTS 过渡到了最新版本;这一变化有两个影响:
对。LTS 更注重可预测性,而不是运行时的稳定性。
尤其是当你是交付链中的一环时,这真的很有帮助。
具体来说,LTS 是指有一组已知的 bug;你可能有一个 bug,意味着某个应用程序会在所有东西上覆盖模糊的小方块,但它始终存在,你不会把这个 bug 换成一个应用程序在连续使用 72 小时后崩溃的 bug。我们的想法是,你可以无限期地忍受 LTS 的“.0 ”版本中的 bug,但你无法应对新 bug 的引入或回归。
>DARPA 正在研究一个将 C 代码自动重写为 Rust 的项目,名为将所有 C 代码翻译为 Rust (TRACTOR)
然后在第二步将输出翻译回 C 语言。这样,你就能从工具中获得修复,而不必现在就强制转换语言。(你仍然可以在稍后的时间切换到更高级别的语言)。
你很可能什么也得不到。众所周知,美国国防部高级研究计划局(DARPA)在资助许多项目时都会考虑到 99% 的项目都会失败,但哪怕只有百分之一的项目成功,那也将是一项重大突破,可能会改变许多事情。
要开发出一种能快速生成代码的实用工具,这将是一项极具挑战性的工作。使用另一种语言作为中间源代码的编译器很可能会在 C 语言的基础上出现问题;通过 Rust 编译的 C 语言编译器可能会在 Rust 中模拟 C 语言的内存系统,通过额外的间接层来增加内存安全性。(或者把所有不安全的代码都变成 Rust 代码,这样就可以使用 Rust 编译器,但不会增加任何内存安全性)。
用 Rust 重写 C 的程序很可能会拒绝完全正确的 C;“printf (string, x); ”是合法有效的(给定字符串的正确运行时内容),但如果没有 printf 函数,就无法转换为 Rust,而这只会将糟糕的代码转换为更糟糕的代码。它不可能神奇地追踪纠缠在一起的指针网,所以会有额外的间接(扼杀速度)、不安全的 Rust 代码,或者直接拒绝代码。
无论如何,一个能将 C 语言重写成 Rust 语言的好工具,对于自动完成大部分转换工作是非常有用的。但如果不进行一些干预,它是无法将不安全的 C 语言转化为安全的 Rust 语言的。
TRACTOR 和类似工作的目标是生成*自动化*的 Rust 代码,而不是某种用 Rust 编写的 C 虚拟机。如果能做到这一点,就需要LLM、形式化验证之类的先进技术。这不可能是简单的机械转换。
LLM 连最简单的提示都不能可靠地写出成语式的正确代码,你凭什么认为它能从 C 代码这样乱七八糟的东西中写出正确的代码?
因为 LLM 做得最出色的一件事就是人类语言翻译。
LLM,至少我试过的所有公开版本,都是糟糕的翻译工具。现在的机器在这方面要比 Magic Goody 时代好得多,但它仍然经常会产生一些无趣的句子,并 “误解 ”上下文(我用引号是因为即使在最好的情况下,它们也无法真正理解自己在做什么),而且在遇到成语和俚语时会完全崩溃。
我的朋友是一名翻译,在一家大型中国公司的本地代表处工作。他们处理大量的技术文档(最初是用普通话编写的),而处理这些文档的唯一方法就是依靠机器翻译。他们选择了他们能找到的最好的解决方案(这是一种商业解决方案,我没有去管它,但如果需要的话可以问他),但如果没有大量的人工编辑,其输出结果是无法使用的。这些年来,他给我寄来了一些例子,有他检查之前的,也有他检查之后的,原始的 “翻译 ”对于没有受过破译训练的人来说往往是无法理解的。真正的不可能,不仅仅是这里或那里的用词不当。
这就是他整天在做的事情。从聊胜于无的意义上讲,这仍然是一个有用的工具(否则他们就不会使用它了),但要达到一个*普通翻译的水平,至少能写出一些连贯的、能表达观点的东西,即使不是莎士比亚式的文字,也还有很长的路要走。
但他们的英语语法比我好得多,这一点我愿意承认。
> 从聊胜于无的意义上讲,它仍然是一个有用的工具(否则他们就不会使用它了),但要达到一个*平均*译者的水平,即使不是用莎士比亚风格写的,至少也能写出一些连贯的、能表达观点的东西,还有很长的路要走。
> 附注:我不知道是什么原因造成了这种效果,顺便说一句:我知道从俄语或德语翻译的文档从来没有那么糟糕(在两个方向上),无论是人类还是 LLM,但中文中的某些东西使其无法在没有完全理解这些句子实际含义的人的情况下进行充分的翻译,没有特定技术知识的普通翻译总是产生垃圾,而 LLM 仍然会这样做。
这大概是因为英语、德语和俄语都是印欧语系语言,因此都是从一个相当近的祖先那里传下来的。我记得读过一些关于 “语言四波 ”的文章,印欧语属于第四波。我认为匈牙利语和芬兰语属于第二波语言,虽然它们的词汇完全不同,但语法结构相似。
我认为盖尔语、巴斯克语和加泰罗尼亚语可能属于第三波。至于汉语、日语等属于哪一波,我还没有头绪。
但问题是,欧洲语言的结构是相似的,所以主要是翻译单个单词,注意习惯用语,将粗糙的翻译转换成好的翻译并不费力。语言塑造你的世界观,就像你的世界观塑造语言一样。一个很好的例子是 “Borgeois、Burgerlich、Middle-class”。三个单词,三种语言,相同的基本概念,但每个单词在其语言中都是独一无二的,虽然一个天真的翻译者可能会认为它们是相同的,但这三个单词的意思却大相径庭。事实上,我甚至不知道其中任何一个词在其他语言中是否有确切的译法。
试想一下,在三种密切相关的语言中。现在把它扩展到更多不同的语言中……
(我在工作中经常看到这种情况–略有不同。我的同事有波兰人、中国人和印度人。由于这些语言使用的声音都不一样,我很难听清楚他们说的话,他们也很难听清楚我说的话)。
干杯
沃尔
这也不仅仅是个别词汇的问题。汉语语法中有许多与印欧语言完全不同的地方,如动词叠加、用方面标记代替时态、名词短语中使用分类词而不使用定语,以及几乎完全不使用转折词(奇怪的是,后者几乎是英语的一个特点,但英语在这里和那里会使用一些转折词,主要是动词变位和复数)。这还不算 “汉语 ”不是一种语言,它是一整套(密切相关的)语言,它们的用法都略有不同。
(对于好奇者:我发现 https://en.wikipedia.org/wiki/Chinese_grammar 是一个有用的起点,但我必须承认,我自己不懂中文,所以我不知道它有多准确)。
是的。这是正确的。令许多翻译系统感到困惑的一点是,中文中缺少语法时态。在英语中,每个动词都必须有一个时态,这是不可避免的。而中文则不然,你必须从周围的语境中获得时态(过去、现在、将来)。
另一个让文件写作者特别头疼的问题是被动语态。中文中很少使用被动语态,除非是说严重的事情(“他被车撞了 ”这类严重的事情)。像 “工作处理完毕 ”这样的句子很难逐字翻译。
请问……你到底抽了什么烟?盖尔语是凯尔特语,是印欧语系的一个分支。加泰罗尼亚语是普通拉丁语的直系后裔 也就是说,也是印欧语系,意大利语的分支。加泰罗尼亚语与西班牙语的距离就像罗比-伯恩斯的语言与 BBC 英语的距离一样远。绝对比英语和荷兰语更接近。
很公平–从我所写的内容可以看出,我实际上并不知道。所以我才故意含糊其辞。
实际上,听你这么一说,我怀疑拉比-伯恩斯的语言与 BBC 英语的距离可能比加泰罗尼亚语与西班牙语的距离要远(我的印象是,加泰罗尼亚语可能是一种被西班牙吞并的原有语言,有点像威尔士语和英语)。有点像威尔士语和英语)。拉比说的是苏格兰语(又称 “盎格鲁人的语言”),而 BBC 说的是英语(又称 “撒克逊人的语言”)。
那么巴斯克语又是什么呢?
干杯
沃尔
拉比说的是苏格兰语(又称 “盎格鲁人的语言”),而 BBC 说的是英语(又称 “撒克逊人的语言”)。
加泰罗尼亚语是一种被吞并的语言–只是它与吞并它的语言关系密切而已。斯考特语和英语也是如此(分化时间相似–15 世纪左右)。巴斯克语是一种非常不同的语言–它完全不是 “I-E”,但更重要的是,其语法的不同足以让事情变得有趣。这与共同祖先无关–我们**知道,I-E 语言的共同祖先曾进行过多次深入的改写,以至于重建其共同祖先的语法几乎毫无希望;巴斯克语的语法特征超出了已证实的 I-E 语言的观察范围。虽然不知道这会给自动翻译带来多少麻烦……
我没有接触过这种波浪理论。但快速浏览一下维基百科就会发现,如果印欧语是第 4 波,那么加泰罗尼亚语肯定是第 4 波(与许多其他语言一样,是方言拉丁语的衍生),而盖尔语源于岛国(爱尔兰、英国)语言,它仍然是印欧语,但可能与伊比利亚语或高卢语有关。但芬兰语肯定不是印欧语,所以可能是第二波,而巴斯克语(目前被认为是一种孤立的语言)可能是第一波(如 “在已知的语言运动之前”–或者应该是 “零波?)
但你说所有这些语言的句子结构都很相似,而中国方言/语言(选择哪种是政治选择)则完全不同,这一点非常正确。正确听音的困难也是如此–例如,在汉族语言或(前汉族语言,如朝鲜语)中,我们说英语的人可以分辨出的音,如 l、n、r,对说这些语言的人来说却很难分辨。
现在我发现其他人比我表达得更好。<sigh/>
好吧,你注意到了我对声音的评论,我想其他人都没有注意到……
干杯
沃尔
LLM本身是不行的,但LLM加上验证器就可以。这就是谷歌为回答国际数学奥林匹克问题的人工智能所做的工作。
编写一个 “这个 Rust 代码是否等同于那个 C 代码 ”的验证器仍然是个挑战,但也许更容易一些。
> 写一个 “这个 Rust 代码是否等同于那个 C 代码 ”的验证器仍然是个挑战,但也许更平易近人一些。
> 写一个 “这段 Rust 代码是否等同于这段 C 代码 ”的验证器仍然是个挑战,但也许更容易些。
事实上,这是无法判定的。这源于赖斯定理(Rice’s Theorem),它是停止问题的扩展。
当然,您也可以得到 “是/否/不知道 ”的结果(这就是静态分析的工作原理)。即便如此,这也将是一个非常棘手的问题。
在一般情况下,这是无法判定的。但我们并不是给验证者一个转换完成的代码库,而是在验证者和生成器(某种生成对抗网络)之间来回切换。LLM 只生成验证者可以证明等价的代码片段。拥有足够多的可验证变换来转换一个真正的 C 项目仍然是一项艰巨的任务,但并非不可能。
这里有一个很大的实际问题,那就是验证者会偏好系统中定义明确的部分,但很有可能解决方案的核心是 C 语言中定义不明确的部分,在这种情况下,验证者会直接以不明确为由拒绝接受,因此无法判断 Rust 是否也做了同样的事情。
举例来说,假设我们有这样一段代码:如果支付的金额少于两笔,则立即返回;如果少于两笔,则忽略最大和最小的支付金额,找出其他支付金额的平均值。验证者可能会认为这段 “求均值 ”代码,即解决方案的实际内容,定义得不好,因为它不确定均值计算中的除数是否为零。如果正好有两笔付款呢?因此,所有这些工作,也就是我们要做的事情,都被放弃了,我们的 Rust 只回答了 “是否有一些付款?”这个琐碎的问题,而将实际工作留作待定。
在 C 语言中,有太多的狭义契约。在 C 语言中,用狭义契约编写函数基本上是习以为常的。在 stdlib 中,这些契约都有详细说明(如果有人读过的话),但在其他人的代码中,这些契约只是无声无息地缩小了范围,你会很难发现这一点。
没错。你不需要解决停止问题,只需要覆盖足够大的范围,就能解决大多数实际问题。终止和 eBPF 校验器也是一样,只需将问题空间限制在你知道能解决的范围内,或者添加一些启发式,在你认为可能永远无法完成的情况下中止,等等。
如果 LLM 生成了 Rust 代码*和* Rust 代码等价于输入 C 代码的正式证明,那么证明是否有效就很容易判定了。
当然,这是一个很大的 “如果”。没有人说这很容易,甚至实际上是可行的。这是一项研究。
如果你能证明任何安全的 Rust 程序都等同于给定的 C 程序,那就意味着 C 程序一开始就不可能有任何内存安全问题。有多少大型 C 代码库(你想转换成 Rust 的代码库)可以有把握地这么说呢?
如果你有这样一个(内存安全)无错误的 C 语言程序,那么是的,你可以使用这样一个假设的转换器生成的 Rust 代码来增加将来引入错误的难度。但是,如果你想转换成 Rust 来剔除现有的、未捕获的问题,那么要求正式的等价性证明不是会让问题简直无法解决吗?
> 如果你能证明任何安全的 Rust 程序都等同于给定的 C 程序,那就意味着 C 程序从一开始就不可能有任何内存安全问题。有多少大型 C 代码库(也就是你想转换成 Rust 的代码库)可以有把握地证明这一点呢?
比这更微妙的是,你可以细化 “等价 ”的概念。例如,你可以使用 “等价,假设 C 语言程序从未触发未定义的行为”(即 C 语言编译器在应用优化时使用的等价概念)。
或者,你可以分析完整的系统,证明对于实际中可能发生的所有情况,某个 C 函数等价于某个 Rust 函数,即使在不同的上下文中,它可能会有不同的行为,做出一些不安全的事情。
TRACTOR 是一项研究提案,而不是 “仅仅 ”需要实现工作的东西,其中一个原因是 DARPA 所追求的 “等价 ”定义是 “在同样的规范下,Rust 程序员会写出什么,而同样的规范导致 C 程序员写出这段 C 代码”。
LLM 起源于机器翻译,在这一领域非常出色;随着提示语越来越多、越来越详细,LLM 在输出中生成成语的能力也越来越强。从 LLM 的角度来看,“最简单的提示 ”是一种语言中可接受的输出的完整工作范例;从 LLM 的角度来看,简短简单的提示很难。有时,他们能从一个小提示中得出一个似是而非的输出结果,但这只是幸运的意外,并不是他们的核心竞争力。
我对 “最简单的提示 ”的定义是,它能以 LLM 所需的方式提供信息,而不需要逐字逐句地说明你希望它产生什么样的输出。老实说,即使是后者,大多数 LLM 也不能可靠地生成正确的输出。
将惯用的 Rust 代码翻译成 C 语言会产生可怕的 C 代码。这实际上是一种语言转换,转换到一种完全陌生、无人问津的语言。
不过,用机械的方式会更容易实现。据我所知,几乎所有的 Rust 语义都可以映射到有效的 C 语言语义,但反过来就不行了。从机制上将安全代码转换为允许但不强制安全的代码,要比将不安全代码转换为不允许不安全的代码容易得多。
最大的例外是异步代码,它需要首先在运行时进行硬编码,才能正确地进行转换。
它的价值是负的,因为生成的 Rust 代码并不比 C 代码安全,维护起来也比 C 代码差很多。
我漏掉了一些东西;建议的方向是从手写的 Rust 代码开始,将其翻译成 C 代码,然后由 FreeBSD 的 “基本系统 ”中的编译器编译。因此,Rust 代码可能比 C 代码更易于维护,因为 Rust 代码是为了便于维护而编写的,而 C 代码则是由翻译器输出的,翻译器接收 Rust 代码并生成与 Rust 代码的所有定义行为具有相同语义的 C 代码。
不清楚新版本的 Rust 构建所有旧版本代码与需要为 base 中的所有代码选择单一版本之间有什么联系。如果某些代码仍在旧版本中,就不会出错,仍然可以编译。
从好处考虑,单一版本确实会让 Rust 的教学稍微简单一些。
如果你说我们所有的代码都是 Rust 2021 版,那么没有人需要知道 2021 版中已经消失的东西,然而他们也不需要学习 2024 版中才有的新东西(但他们确实仍然需要学习那一时期的新东西,但它们并不是版本的一部分,它们只是新的)。
原则上,维护 2015 版代码的人应该知道,该代码中的 Box<Goose> 可能意味着我们今天写成的 Box<dyn Goose>。你不能在现代版本中这样做,但在 2015 年,人们会这样拼写该类型,而且他们已经习惯了。
同样,你的 2021 版代码知道数组是 IntoIterator — 当然,每个人(使用现代 Rust 编译器)都可以迭代数组,但在 2018 版或 2015 版代码中,却假装不知道为什么这样做,以确保与人们以前写的另一种老式东西兼容,当时数组真的不是 IntoIterator,而是对整个数组作为切片的引用。
实际上,我认为 FreeBSD 希望使用 MSRV 承诺来完成这项工作。也许 FreeBSD 14.1 会承诺使用 Rust 1.79,而 FreeBSD 14.0 只承诺使用 Rust 1.70。为这样的系统编写工具的人会判断他们是否应该保守(这可能意味着更多的工作)。
不过总的来说,我在这次对话中看到了一些我在与 C++ 程序员讨论时经常看到的情况,他们似乎认为 Rust 的频繁发布意味着该语言的一切都在变化之中,而同时又认为 C++ 缺乏稳定性无关紧要。我注意到讨论中没有提到 FreeBSD 的基本系统中使用的是哪种 C++ 语言。C++ 98? C++ 14? C++ 23? “不管 Clang 做什么 -shrug-” ?
C++ 每三年就会推出新的语言版本。尽管在实践中,这些语言与用户所期望的非常相似,但实际上并没有正式承诺过这种通用性,尤其是在大型代码库中,尽管软件非常复杂,但人们可能更看重长时间内的稳定性,而 C++ 并不总是能满足这种期望–它没有承诺过,所以也没有食言。
例如,Herb Sutter 为 C++ 20 添加了 <=> “飞船 ”操作符而感到自豪。这个运算符的作用类似于 Rust 的 PartialOrd Trait。你只需实现一次,就能一次性获得所有比较运算符,并保证它们的一致性,这对新的 C++ 软件来说是个胜利。当然,已经有一些 C++ 代码对现有的比较运算符进行了奇怪的定义,其中一些代码在新的 C++ 语言中 “崩溃 ”了,要么是因为 Herb 的特性,要么是因为 stdlib 中的连锁反应。也许这些代码在技术上本来就是错误的(大多数 C++ 都是如此),但对其所有者来说,C++ 20 却破坏了它。
我绝对同意使用单一版本更易于使用和学习。只是看起来情况并非如此;似乎有人认为,为了兼容性,*有必要使用单一版本。
是的,人们似乎都认为 Rust 在升级时会出现与他们使用过的其他语言一样的故障,而不考虑一种语言在兼容性方面做得更好的可能性。
> 是的,看起来人们确实在假设 Rust 在升级时会与他们有经验的其他语言一样出现故障,而不考虑一种语言在兼容性方面可以做得更好的可能性。
> 姑妄听之,单一版本确实会让 Rust 的教学变得稍微简单一些。
我认为版本讨论中缺少的一个注意事项是,对于这种以稳定性为最重要资产的项目,你最多应该坚持使用当前版本-1:当前版本在技术上是实时的,虽然总是向后兼容,但并不一定向前兼容,因为扩展在某种程度上是允许的。stdlib 也是如此。你可能会发现自己处于这样的情况:你愉快地用 Rust 1.80 在本地编码,使用了 1.78 没有的功能,但 base 使用的是 1.78,所以你破坏了构建,尽管两者使用的是同一个允许的版本。幸运的是,所有之前的版本都已冻结,因为新开发都是在当前版本进行的。
这是因为 Rust 并不是为使用旧编译器而设计的。它的设计初衷是让新编译器始终能够处理你的旧代码。
> 我认为版本讨论中缺少的一个注意事项是,对于这种以稳定性为最重要资产的项目,你最多应该坚持使用当前版本-1:当前版本在技术上是实时的,虽然总是向后兼容,但不一定向前兼容,因为在某种程度上允许扩展。
在版本讨论中,我发现缺少一个注意事项,那就是对于这种以稳定性为最重要资产的项目,您最多应该坚持使用当前版本-1:当前版本在技术上是实时的,虽然总是向后兼容,但不一定向前兼容,因为在某种程度上允许扩展。
> 幸运的是,所有以前的版本都已冻结,因为新的开发都是在当前版本进行的。
即使你使用的是旧版本,你所描述的情况仍有可能发生。即使你的 Rust 代码使用的是 2018 版,1.80 编译器也会很乐意让你使用 1.80 开发周期中引入的 stdlib API。版本涉及语法的不兼容更改、默认导入的更改等。它们并不是要在时间上冻结语言(广义上)。
我的理解是,他们想为 Rust 代码设定一个*最大*版本,以便开发人员可以使用 2024 年版或更早的版本,但不能使用 2027 年版(直到他们更新了要求)。
考虑到 Rust 目前功能稳定的方式,我认为最低支持版本会更好–我能想到的指定版本的唯一好处是,它可能更容易与 gcc-rs 等其他实现集成……不过我想,在我们有实际有用的替代实现之前,我们不会真正知道如何实现。
Rust 版本真的能解决这个问题吗?我的印象是,破解可能会发生,也确实会发生,但 Rust 维护者或 Rust 政策并没有将其视为在发布前解决的重要问题,甚至没有在下一个版本中破解前在上一个版本中发出警告(无论如何,现在还没有)–最近的例子是 https://github.com/rust-lang/rust/issues/127343,它破解了旧版本的时间 crate,从而破解了任何依赖于它的东西。这需要下游维护者积极更新他们的锁定文件来修复,而且似乎与版本无关。
显然,如果我们考虑到 FreeBSD 基本版本不会依赖于 crates.io 上的 crate,那么时间 crate 回归本身就不在这里的讨论范围内了,但值得注意的是,由于不在 crates.io 上,因此任何 FreeBSD 基本版本的破坏对于 Rust 回归工具来说都是完全不可见的。
时间 crate 故障是一个异常可怕的故障,我们正在对流程和语言进行多项改进,以确保今后不再发生。(简而言之:只有一种可能类型是答案的情况变成了不止一种可能类型是答案的情况,导致了类型推断失败,我们正在努力主动捕获并避免类似的情况)。
与其他大多数软件发行版相比,FreeBSD 的一个巨大优势是他们将所有代码集中在一处,可以在升级工具的同一提交中为所有软件打补丁。
> 我们
出于好奇,你是项目中众多乔希中的哪一位?
令人难以置信的是,每当 Rust 发生令人遗憾的事情时,同一个 Palantir/“大宏观 ”的人就会出现,并做出最糟糕的决定,但他似乎仍然不可触碰。
你是说 dtolnay?嗯,他是 serde 的负责人,而 serde 是我最喜欢 Rust 的地方之一,所以他应该得到很多松懈。但我同意你的看法,我非常不同意他最近的一些决定。
他为 Rust 生态系统做了很多了不起的事情,这也令人难以置信。只要比较一下 https://crates.io/ 和 https://crates.io/users/dtolnay?sort=recent-downloads 的 “下载次数最多”:下载次数最多的前 3 个 crate 都是他做的,这还不包括 serde!
他做了很多工作,也犯了一些错误。如果你只看Rust在有呼声时发生的事情,当然只会看到他的错误。
这就是极有才华的人的问题所在:他们习惯于在对手错的时候自己是对的,以至于在极少数情况下,当他们真的错了的时候,需要付出绝对可笑的努力才能说服他们,是的,这次他们真的错了。
就我个人而言,当我看到这个列表时,我看到的是一堆宏黑客,在一个理想的世界里,这些宏黑客本来就不会存在,或者至少不会被当作完美的永久解决方案。我们在改进编译时间方面也付出了很多努力,但这些巨型宏crates却占了整个编译时间的很大一部分,它们是不可触碰的。
如果这完全与他无关,也不在他的控制范围内,那就不会这么苛刻了。我们只能尽力而为。但事实并非如此,因为他是出于一种不为人知的种族歧视,以及一想到他的宏王国将变得不那么重要时的得意忘形,故意、秘密地破坏了在语言中添加反射的工作。然后花了大半年的时间,让其他比他更正直、更可爱的人(其中有些人我还把他们当朋友)替他背了黑锅。
因此,我们得到的不是一个人人都能在语言中使用的伟大功能,而且不巧的是,这个功能还能让很多事情变得微不足道,甚至不需要成为一个库,但我们得到的却是这个。而每个人都说,你不能批评他,因为他卖的是 “十大 crates”,这是他造成的局面。
如果这听起来很痛苦,是的。没错。
> 相反,他故意暗中破坏在语言中加入反射功能,其原因不详,既有种族主义的因素,也有一想到自己的宏王国将变得不那么重要就得意忘形的因素。
听起来很辛辣。我能在哪里读到更详细的资料吗?
我想这是你能找到的最好的资料了。至于到底是谁在什么时候对谁做了什么,这个问题还没有答案(而且我也不确定能不能回答这个问题)。
RustConf 2023 主题演讲的混乱和 “破坏反思的添加 ”是一回事吗?
我一定是忽略了更多的上下文。
主题演讲是关于正在进行的为 Rust 添加反射的工作。而这项工作在主题演讲惨败之后就停止了。
有了你和 atnot 的回复,我终于把这些点联系起来了。
真是太遗憾了。
值得注意的是,这是在揭露 dtolnay 的参与程度和他的灾难性反应之前。此外,它还花了大量时间来分析与受影响的人无关的事情,比如事情发生的具体机制,以及谁在什么时候出于什么动机说了什么。
以下是我对时间线的简要概括:
– 许多人强烈鼓励 Thephd 等人进行反思
– 他们突然收到某个决定被推翻的消息,没有人愿意为此负责,也没有人愿意告诉他们是怎么回事。他们嗅到了 “内鬼 ”的味道,要求从技术上解释为什么工作被认为不合格。
– 如果得不到这样的解释,他们就会离开,因为他们能正确地察觉到有人在幕后操纵他们的蛛丝马迹(任何生活在美国的黑人都会敏锐地察觉到这一点)。
– 大起大落,每个人都指责别人,这不是任何人的错,这实际上是合理的担忧,等等(fasterthanlime 的帖子就发生在这里)
– 很多人因为放任这种情况发生和/或保护肇事者(没有人指名道姓)而从各种岗位上退下来。
– 很久之后,有消息称 dtolnay 其实在幕后操纵,只是让大家替他背黑锅。对此,他在 github 上发表了一篇充满可证实谎言的奇怪文章。然后向 Thephd 询问如何解决这个问题。他们再次要求对他们的工作进行技术批判。
– Dtolnay 无法提供。thephd 重申,在做出这样的技术批判之前,他们不会回到 Rust。即使在 David 的名字广为人知之后,这种情况也没有发生,这证明了 Thephd 的预感,即这样的评论根本不存在。(https://cohost.org/ThePhD/post/7169013-weird-question)
今天的情况依然如此。
(但如果你有太多、太多、太多的时间,这是我所知道的试图总结所发生事情的最新资料:https://dragon.style/@pyrex/111005018693053136)
> (但如果你有太多、太多、太多的时间,这是我所知道的试图总结所发生事情的最新资料:https://dragon.style/@pyrex/111005018693053136)
总结的总结:
1. 人们为了获胜而牵线搭桥。可悲但不幸的是,一切照旧。即使是世界上最好的语言,也会受到政治和非技术问题的影响。
2. 牵线的人是种族主义者,因为…… “受害者”[*] 是黑人,而邪恶的家伙是白人。哇哦 如果你发现了我遗漏的任何具体内容,请指正我。如果没有,那就是纯粹而明显的诽谤。
具有讽刺意味的是,种族主义指控是在一卡车其他更有证据的指控之后提出的,而这些指控十有八九足以解释所谓发生的事情。
也没有任何切实的证据可以证明这家伙不是种族主义者。我天真地以为,在这样一个空虚的环境中,每个人都有权不被称为种族主义者,但也许我没有花足够的时间在这个新的社交媒体上。
[*]引号是因为这个词通常用于比被 Rust 拒绝更大的生活问题。
在我看来,自由党团队的工作真的很失败。
在过去,我不认为这种倒退会被容忍。也许有些东西已经改变了。
据我所知,RFC 1105 在技术上是允许这种破解的: https://rust-lang.github.io/rfcs/1105-api-evolution.html
请注意,“技术上允许 ”并不一定等同于 “实践中的好主意”。RFC 本身对这种区别提出了警告。现在的情况似乎是,Rust 的人正在重新评估 RFC 是否是一个足够好的标准,或者是否应该为类似情况制定更详细的规则。在这种情况下,语言开发者的做法是正常而健康的。
多此一举: 在我看来,这样的文档的存在也说明了为什么 SemVer 使用 “必须”(MUST)而不是 “应该”(SHOULD)是错误的。如果您意图禁止做一些实际上并不是您的标准 “工作 ”所要求的事情,那么人们就会引入更多的定义来解释为什么他们并没有真正做您认为禁止的事情。在 Rust 的上下文中,SemVer 有效的 MUST(即人们真正关心的 MUST,而不是显而易见的 “不要使用毫无意义的不规则编号 ”的 MUST)是事实上的 SHOULD,而在实践中,我怀疑其他大多数声称遵循 SemVer 的项目也会将这些 MUST 作为非常强烈的 SHOULD 来对待。但至少 Rust 是诚实地这样做的(并且明确定义了这样做的边界)。
比 MUSTs 被 SHOULDs 更糟糕的是,实际上大部分生态系统都是 zerover[0],这当然是一个笑话的名字,指的是为了避免遵守 semver 而从不发布 1.0 版本的现象,这反过来又使得大部分生态系统无法长期使用。
真正有趣的是,在 Rust 的世界里,不发布 1.0 版本并不能让你摆脱 SemVer 的束缚:Cargo 只是假设,如果某个 crate 的主版本为零,那么下一个数字就是实际的主版本。
> Poul-Henning Kamp 说,支持 Rust 的观点可以简单归结为“‘所有酷小孩都这么做’”。
我必须承认,我很震惊人们在 2024 年还在以这种方式谈论 Rust。内存安全不是 OOP,也不是时不时充斥软件工程领域的任何其他时尚。内存安全是强制性的。你必须关注它,或者你的语言必须为你关注它。别无他法。Rust 提供了一种独特的零成本抽象,使绝大多数程序都能以类似 C++ 的速度运行,而完全不需要关心内存安全。这就是使用 Rust 的理由: 它提供了大多数(如果不是全部)其他编程语言所不具备的具体、有形的优势,这对每个分配内存的非复杂程序都很有用。Rust 还有其他一些不那么有趣的特性,这些特性也使它成为一门优秀的语言,但借用检查是 Rust 与众不同的原因,而不仅仅是另一种 “没有弊病的 C 语言”。
当然,对于 Rust 的优势到底有多强,以及它如何适用于 FreeBSD 的具体情况,我们还有争论的余地。但从 Kemp 的整个邮件来看,他似乎并没有认真对待这一论点。
至于要求人们重新实现某个大型软件的建议,否则我们不会认真对待你: 我真的很想相信,在这些讨论中,每个人都是在真诚地争论。我不想相信坎普是在认真倡导一种制度化的欺侮仪式,这样人们才可能在这个领域得到认真对待。但我实在不明白,他希望从这样的努力中得到什么有用的数据,或者说,要求人们在撰写提案之前做如此大量的工作还有什么其他用处。
为了更好地了解语言的实际使用情况(以及语言对外部库的依赖程度,坎普也提出了这一完全正确的观点),要求查看非语言自身的 stdlib 或编译器的有用实现是有一定道理的。但你不必要求提出建议的人同时也是该实现的作者–这样做的好处充其量只是微不足道,而且会造成严重的限制。所以,我真的不知道坎普到底想说什么,也不知道他为什么会认为这是个好主意。
> 内存安全是必须的。
我同意,但你似乎忽略了文章中开发人员决定不使用 C++ 智能指针“”因为我不太擅长 C++“”的部分。
一边鼓吹 Rust,一边又积极回避 C++ 中的内存安全特性,以支持 Rust 的使用,这真是荒唐至极。这似乎是双重标准。
> 似乎存在双重标准。
> > 似乎存在双重标准。
> 我不明白为什么。
我想,Rust 代码如果不惜使用 “不安全”,就会在审查中被指出而不被允许。那为什么允许 C++ 代码使用 “原始 ”指针?
> 但是智能指针呢?它们不是内存安全特性!它们是方便功能!即使使用 unique_ptr(C++ 中最接近 “安全特性 ”的东西),你也可以同时使用悬空指针和双空闲指针,string_view 或 span 使它们比 C 风格的原始指针更容易使用!别再提可选指针和访问指针了!
我认为 “智能指针 ”指的是 std::shared_ptr,它与 unique_ptr 完全不同,更像是 Rust 的借用检查器。
> 对于一个多年前就不再积极关注 Rust 的人来说,“让我坚持使用我百分百熟悉的东西 ”不失为一个好选择。
如果这就是他们想做的事,那对他们来说是好事,但这并不意味着他们可以提交垃圾代码。
> 那么为什么允许 C++ 代码使用 “原始 ”指针呢?
因为在 C++ 中,“this ”就是一个原始指针。实际上,所有的 C++ 引用都是如此 — 没有任何东西可以阻止被引用对象消失,留下一个悬空引用。
原始指针或引用的另一个主要用途是函数的外参数。没有人会在这里使用智能指针。
不在任何地方使用 “this ”或引用的 C++ 方言确实非常奇怪。没有人会这么做。
> 我认为 “智能指针 ”指的是 std::shared_ptr,它与 unique_ptr 完全不同,更像是 Rust 的借用检查器。
std::shared_ptr<T>是 Rust 的 Arc<T>:线程安全的引用计数,运行时开销很大。std::unique_ptr<T>是 Rust 的 T 的简陋版本:具有移动语义的单一所有权,运行时开销几乎为零,你可以借用一个非所有对象的引用(在 C++ 中是 T& 或 const T&,在 Rust 中是 &mut T 或 &T)。(但是,std::unique_ptr 更糟糕,因为它只支持堆分配的对象,而不支持栈分配的对象;而且它的运行时开销更大一些,需要检查 nullptr 以确定是否仍拥有该对象,而 Rust 在编译时就会跟踪所有权;unique_ptr 还提供了很多违反内存安全的机会,尽管它仍然比原始指针安全得多)。
std::shared_ptr 和 std::unique_ptr 通常都被称为智能指针,包括 C++ 规范本身(第 20.3 节)。而 FreeBSD 帖子的上下文是在单个函数中分配和释放对象的代码,因此 std::unique_ptr 更为合适(或者也许是 std::vector,因为它们大多是数组)。
它更接近于 Box<Option<T>>,但当你取消引用时,它会隐式调用 Option::unwrap_unchecked()。这在 Rust 标准中是非常可怕的,但在 C++ 标准中却非常普通。
从技术上讲,这是正确的,但我的意思是(但并没有真正说出来),从习惯上讲,我认为你通常会在与 Rust 中简单使用 T 相同的地方使用 std::unique_ptr<T>。
例如,在 C++ 中,你可能会避免使用原始 T,因为它有一个昂贵的复制构造函数而没有移动构造函数,或者因为你怀疑它可能会这样,而它的文档并没有提供明确的答案,或者你正在编写泛型代码,所以你无法事先知道。特别是,我认为使用 std::vector<std::unique_ptr<T> 来确保向量增长时不会出现不必要的复制是相当常见的,而在 Rust 中,你只需使用 Vec<T>。
std::unique_ptr<T>可以用在像 pimpl 成语这样的地方,即一个公有类的成员变量,其中 T 的定义是私有的,不应该被该类的用户访问。在 Rust 中不需要这样做,因为公有结构体可以有私有类型的字段,而可见性规则会将实现隐藏起来。(这确实意味着私有类型会成为公共 ABI 的一部分,但 Rust 并没有提供 ABI 稳定性)。
std::unique_ptr<T> 可以用于延迟初始化的成员,在这种情况下,你不想(或不能)调用 T 的默认构造函数,所以你让 unique_ptr 为空,直到它被初始化。在 Rust 中,你可以使用 Option<T> 代替它,虽然它不是 T,但它比 Box<T> 更接近 T,因为没有堆分配。
当 T 过大而无法轻松放入堆栈时,std::unique_ptr<T> 可用于局部变量。在 Rust 中,运气不好–当你创建一个新的 Box<T> 时,编译器可能会在堆栈中构建一个临时 T,然后再将其 memcpying 到堆中。你需要确保你的环境有较大的堆栈,然后你可以直接将 T 保留在堆栈上,跳过装箱过程。(避免定义大型结构体;例如,永远不要使用静态大小的大型数组,而应使用 Vec)。
在某些情况下,你仍然需要一个单独拥有的 Box<T>,比如多态类型(Box<dyn Trait>)或递归类型(二叉树等),但我认为与 std::unique_ptr 的所有其他用途相比,这种情况很少见,因为它们不应该被转换为 Box。
> 我认为 “智能指针 ”指的是 std::shared_ptr,它与 unique_ptr 完全不同,更像是 Rust 的借用检查器。
这里有几条评论试图将 C++ 的 std::unique_ptr 和 std::shared_ptr 映射到 Rust 中,我认为他们都没有真正搞清楚。
std::unique_ptr<Goose> 是 Box<Goose>。这只鹅生活在堆上的某个地方,当我们用完它时,我们需要销毁堆分配,以及应该发生在鹅身上的任何事情(如果有的话)。这两种类型都提供了将实际原始指针取出和将原始指针转换回类型的机制,它们是非常并行的。如果使用 Box::leak(a_goose) 就更容易理解了,但 std::unique_ptr 也有同样的 API,只是名字不那么好解释而已。
std::shared_ptr<Goose>是 Arc<Goose>,或者在某些工具链上的少数情况下,神奇地用 Rc<Goose> 代替–如果是这样的话,希望工具是正确的。在堆上有一只鹅,当我们 “复制 ”它时,我们会再次得到同样的鹅,但我们要计算有多少对它的不同引用存在,一旦没有了,我们就可以删除这只鹅。同样,API 也是并行的,std::weak_ptr 就是 Weak<T>,最本质的区别在于 C++ 提供了一种机制,可以显式地判断控制块是否属于同一内存分配的一部分。如果是,那么即使 std::shared_ptr<Goose> 已经 “消失”,我们也不能释放内存,因为我们的控制块还在内存中,而任何剩余的 std::weak_ptr 都需要控制块;但另一方面,如果不是,那么我们就是在为一个项目及其相关的控制块进行单独分配。
在 C++ 程序员看来,“更现代 ”就是更安全,而事实可能并非如此。例如,Arc<Goose> 不允许多个可变引用,但 std::shared_ptr<Goose> 却可以,当然,尽管 C++ 程序员知道他们不应该犯错,但他们也是人,对同一个 Goose 的两个引用可能会在没有 happens-before 的情况下被修改,现在我们就有了一个数据竞赛,这就是未定义行为(Undefined Behaviour)。
> C++ 程序员认为 “更现代 ”更安全,其实不然,这是一个问题。
例如,std::span 和 std::string_view 都很现代。但是,与 Rust 中的 &[T] 和 &str 不同,它们当然是非常危险的,因为在 Rust 中,它们是借用的,所以要进行检查,但在 C++ 中,它们不需要检查。
你可以说它们并不_不_安全,但即便如此,它们也并不_更_安全,它们只是更现代而已。
对于 string_view(字符串视图),我们知道有些人使用了浪费但正确的代码(不必要地复制字符串),并通过使用底层字符串可能在使用中消失的 string_view(字符串视图),将其转换为脆弱或完全错误的代码。
> 你可以说它们并不不安全,但即便如此,它们也并不更安全,只是更现代而已
已在另一条评论中提及,在此重复+扩展,以提高可见度并澄清问题:
C++ 智能指针与 Rust 智能指针不能直接类比,因为 C++ 智能指针有一个空状态,而 Rust 智能指针没有。这是 C++ 移动语义*发挥作用的必要条件。因此,所有 C++ 智能指针事实上都等同于 Rust 的 Ptr<Option<T>>(或者可能是 Option<Ptr<T>>),而不是 Ptr<T>(对于 Ptr 的适当值),但由于 C++ 是不安全的,因此每次取消引用时,它们都会隐式调用 unwrap_unchecked()(或 as_ref().unwrap_unchecked()等)。
* 在 C++ 中,移动一个对象不会影响原对象的生命周期。移动构造函数与复制构造函数类似,只是允许/期望它占用原始对象的资源。原对象继续存在,直到它被销毁,然后运行它的析构函数(如果有的话)。由于我们不希望 unique_ptr 在每次移动时都释放双倍资源,所以我们必须让它处于空状态,在这种状态下它不会释放任何资源。在 Rust 中,移动一个对象会使原来的对象在移动完成时神奇地消失,根本不需要运行析构函数。实际上,这并不神奇,因为 Rust 是一种系统语言,所以这一切都有明确的规定,你可以在不安全的 Rust 中手工实现。移动一个对象就像通过 memcpy 一样实现,一旦字节拷贝完成,原始对象就是 “垃圾”(也就是说,你不能访问它,也不能以其他方式与它交互,甚至不能运行它的析构函数)。你可以在 Rustonomicon 中实现 Vec 的部分看到这方面的工作示例。在安全的 Rust 中,编译器会自动处理这些细节,并使其看起来像是对象 “神奇地消失了”(如果你在移出对象后试图与之交互,编译器会抛出一个编译错误,这是一种非常聪明的方法)。
> 一边鼓吹 Rust,一边又积极回避 C++ 中的内存安全特性,以支持 Rust 的使用,这太荒谬了。这似乎是双重标准。
萨默斯可不是这么说的。他说他避开一些 C++ 特性是因为他不熟悉它们,而不是因为他想使用 Rust。他也确实说过,如果可能的话,他会用 Rust 来代替,但这与故意破坏 FreeBSD 的 C++ 以使其与 Rust 相比处于劣势完全是两码事(这似乎就是你所描述的)。我不认为有人可信地指责他这么做。
我想,有人可能会说,一个志愿者选择花时间学习 Rust 而不是 C++ 是错误的,或者说一个人喜欢学习其中一种而不是另一种是 “不公平 ”的。就我个人而言,我并不愿意提出这样的论点,因为这让我觉得太自以为是了。志愿者可以决定如何花费自己的时间。
我想把你的论点理解为更合理的东西,但我很难想到它还能意味着什么。
我没有指责他破坏 FreeBSD 的 C++。我的意思是,除了跳转到 Rust 之外,还有一些不那么极端的方法来获得内存安全性,但他没有去使用它们。
智能指针(std::shared_ptr、std::unique_ptr 等)、ASAN、valgrind、coverity 和 linters 等都有助于提高 C++ 的安全性,但并不需要全新的语言和编译器基础架构。
而所有这些方法都需要多年的时间投入,在内存安全方面,目前可以说是死路一条。
请注意,他并没有专门写 C++;他在 C 代码中使用了一些 C++ 的特性,但他熟悉到足以自信的语言是 C 和 Rust。
> 除了跳转到 Rust 之外,还有一些不那么极端的方法可以获得内存安全性,但他没有费心去使用它们。
从个人开发者的角度来看,在 C++ 中获得内存安全比在 Rust 中获得内存安全更极端。要达到与 Rust 相同的安全水平,C++ 需要更多的知识、工具和努力。你不能责怪 Somers 在一种能完成更多工作的语言上投入更多技能。
> Poul-Henning Kamp 说,支持 Rust 的论点简单归结为“‘所有酷小孩都这么做’”。
是的,这部分让我有点生气。这只是居高临下的精英主义,背后没有任何论据。后来也有一些反对的合理论据,但这不是其中之一。
> 是的,这部分让我有点生气。[……]后来也有一些相当合理的反对论据,…
最 “愤怒 ”的人应该是 PHK 本人,因为他用这样的介绍破坏了他所有的合理论据。
> 我还建议,下次如果有人主张导入某种 “所有酷小孩都在做的语言 ”或其他语言,我们甚至都不要看他们的建议,直到他们通过忠实地用这种语言重新实现 cvsup 来证明他们在这种语言上的技能和奉献精神,并记录下这种语言如何以及为什么比 Modula-3 更适合做这种语言。
读到这里,我很庆幸我没有在任何地方使用 FreeBSD,如果这就是项目中重要人物的言论水平的话。
另外,为什么会有人想要重新实现一些古老的 CVS 相关工具呢?这可能对 FreeBSD 仍然很重要吧?
我认为您忽略了很多上下文,所以才会反应过度。
> 我会帮您纠正的: 另外,为什么会有人想要重新实现一些古老的 CVS 相关工具呢?
>
> 这个问题还需要回答吗?
这确实提出了一个问题,为什么 FreeBSD 在这个时代还要依赖 CVS?
尽管如此,Modula-3 似乎是一种比 C 或 C++ 更理智的语言(我是作为一名专业的 C++ 开发人员说这番话的,我在安全关键硬实时领域工作了十多年,现在是 Rust 的忠实粉丝)。
从维基百科上关于 Modula-3 的例子来看,它让我想起了 Pascal/Ada 和大写的 Fortran 的混合体。我个人更喜欢一些不那么啰嗦、功能性更强的语言,但听起来它至少在某种程度上是内存安全的。
在当时,这可能是一个明智的选择(其他选择应该是 Ada 或脚本语言吧?) 现在,它将因缺乏生态系统而受到影响,这意味着了解代码的人更少,可用的库更少(意味着你必须自己编写一切)。
我认为,在任何人承担用 Rust 重写 CVSup 的大量工作之前,都有一些明智的预防措施
– 是否有人 “买账”?CVSup 的用户是否需要 Rust CVSup,还是当你完成工作后,它只会被放在 “我们为 Rust 开发者设置的恶作剧任务 ”堆里?
– CVSup 目前的行为是否有很好的特征描述和文档记录,甚至是否有单元测试,让我们可以知道我们的 Rust CVSup 事实上是否是一个有效的替代品?
– 是否有 CVSup 做不到的事情 Rust CVSup 可以解决?或者它同样做了一些不该做的事情?
我的猜测是,事实上这些只是 “顶层设计”,在实践中,人们对 Rust CVSup 并没有实际的兴趣,因此这项工作将是徒劳的。
请注意,链接的 Perforce 页面只是告诉您 FreeBSD 已经多年没有使用 Perforce 了。这是遗留文档,FreeBSD 的大多数文档都可以描述为 “有人写了这个,没有人负责确保它仍然正确,我们也没有记录它是关于什么或为什么要写它。祝你好运”。也许 CVSUp 是刚刚推出的令人兴奋的新软件,也许出于平台兼容性的考虑,它正被 C 语言等同程序所取代。也许这两种说法都早已过时。
从 “用<语言>完成的第一项基础工作很可能是最核心的工作之一吗?”就可以看出,这只是一种抑制手段。这只是为了提高入门门槛,与 Rust 的初衷恰恰相反。这就是居高临下的把关。
> 要求重新实现一个非常重要的工具,而这个工具由于某些深不可测的原因是用 Modula-3 编写的,这听起来像是一个非常合理的要求。
但这不是我们的要求。我们的要求是让那些有兴趣让 A 语言在基础版中被采用的人重新实现一个用 B 语言编写的工具,而 B 语言近 15 年来一直没有得到持续发展,也从未被大量采用。
因此,假定几乎没有人懂得 B 语言,这似乎是绝对公平的,这意味着我们的要求实际上是将这种语言学到相当熟练的程度。这在某种程度上证明了采用 A 语言的价值?
你真的试过改写东西吗?我的意思是:在你的生活中试过吗?
如果我是魔鬼代言人的话,我想你的意思一定是:“我们不知道 Rust 十年后还会不会存在,让我们看看你是怎么处理我们以前早期采用 Rust 时所犯的错误的,这样你就知道万一 Rust 也变成了一种古老晦涩的语言,我们会签什么合同了”。不过话虽如此,这种说法在我看来已经过时了。如果说 “所有酷小孩都这么做 ”是一个公平的论据的话,那么它在 10 年后还存在的可能性就是一个公平的论据。当 “所有酷小孩都这么做 ”时,就会产生大量未来必须有人处理的遗留代码,这意味着必须有人为其维护工具链。
> 摘自 FreeBSD 维基百科: FreeBSD 混合使用 CVS 和 Perforce 来管理不同的源代码树和项目; CVS (用 cvsup 扩展) 是 “权威的 ”修订控制系统, 它包含四个完整而独立的版本库 (src、 ports、 projects、 doc), 但它在大量分支的独立开发方面有很大的局限性 (重点是我的)。
这篇文章是出于历史原因而存在的(如顶部横幅所示),实际上与现在并不相关。FreeBSD 已经从 CVS –> SVN –> Git 迁移,最近一次迁移是在 2020 年左右。
如果这是真的,那我不得不同意 taladar 的说法,并跟着他重复一遍: 我很高兴没有在任何地方使用 FreeBSD。
FreeBSD 使用 git 有什么问题吗?你是如何远离任何使用 git 的项目的?
使用 Git 没有错。但要求重写一个你已经不再使用的工具就是不诚实了。
Modula 3 工具链一直是维护者的噩梦。用它来编写一个重要的(当时的)工具是一个令人痛苦的错误;真正的解决办法是放弃 CVS,同时停止对 cvsup 和 modula 3 的支持。一个中间方案是 “去他妈的 m3,让我们用 C 语言重写 cvsup 中我们真正需要的部分,这样整个系统就不会成为那恐怖东西的人质”(csup)。我怀疑 phk 的观点与其说是对 rust-in-core 系统倡导者的字面要求,不如说是在提醒人们,上一次有人认为 “这是一门多么好的语言 ”就足以让系统依赖于不稳定的工具链。
他们用 m3 为这个错误付出了惨痛的代价。当然,Rust 的工具链远没有那么恐怖(你可以自己去查–它确实可以作为如何不做语言工具链的一堂课),但 cvsup 的故事一定留下了非常痛苦的伤疤。
我个人不会使用任何仍在使用 CVS 的项目。诚然,CVS 比 Subversion 略胜一筹,但与不太流行的现代版本控制系统相比,它仍然是一个极其糟糕的版本控制系统。
> CVS 比 Subversion 稍微好一点
什么?就我个人而言,我发现 SVN 比 CVS 进步了一大步。早在 SVN 1.0 之前,我就从 CVS 转向了 SVN。当然,现在我已经完全使用 git 来处理所有事情很久了,我不想再回头看了。别误会我的意思:我对 SVN 有很多批评意见,但我对 CVS 比 SVN 好的说法完全不理解。你能详细说明在你看来 CVS 比 SVN 好在哪里吗?
Subversion 并不稳定,至少在它是 CVS 的唯一替代品的那段时间里是这样。尤其是比较容易设置的后台(我忘了名字了)有一些损坏错误。我的模糊记忆是,当它可靠到足以信任的时候,已经有了其他更好的替代方案(即分布式 SCM)。
最初的后端使用 BerkeleyDB,有时会出现 “楔入”,需要人工干预。随后,他们很快使用了 FSFS(文件系统上的文件系统),它使用不可变文件来表示修订。这个后端曾经(现在仍然)坚如磐石。这比 git 早了好几年。
是的,BDB。
谷歌搜索显示,FSFS 是在 SVN 1.1(2004-09-29)发布的。显然,任何被 SVN 中的 BDB 实现所伤的人都会等上一段时间,看看 FSFS 如何,然后再加入进来。当时,DSCM 运动已经开始,monotone 和 Darcs(从 Arch 演化而来)也已存在,这让考虑更换 SCM 以了解情况的人更加犹豫不决。不到 8 个月后,我们就有了 git(不久后又有了 mercurial)。
2002 年到 2003 年左右,SVN 似乎是 “用什么取代 CVS ”的答案。但它漏洞百出。等到 FSFS 可用且值得信赖时,一切都为时已晚。
显然,我真的很幸运。我记得在 2000 年代初(大约从 2001 年开始),我就经常使用 SVN,虽然 FSFS 的过渡比 BDB 快了不少,但我从来没有真正遇到过 BDB 后台的问题(至少在 20 年后的今天我还记得)。
此外,虽然 git 是在 2005 年启动的,但我记得我是在 2006 年初(大概)看到它的,当时我立刻就不喜欢了,因为当时的用户界面太糟糕了(我个人认为)。我不记得具体时间了,但我是在 2008 年或 2009 年的某个时候才再次关注 git 的,那时的用户界面已经好很多了。
因此,我在 SVN 上至少有 7 年的美好经历。当然,现在回过头来看,SVN 的设计有很多不足之处,而且从今天的视角来看,我可能会对当时使用 SVN 的经历做出截然不同的描述。但在当时,我并没有太多可抱怨的。我绝对不想从 Git 回到过去。
我完全理解你为什么出于历史原因没有认真考虑将 SVN 作为 CVS 的继任者。但我仍然对我最初回复的 CVS 比 SVN 略胜一筹(现在式)的说法感到困惑。这可能是 2003 年 FSFS 还不存在时的说法(由于 BDB 中的错误),但现在呢?
但我仍然对我最初回复的 CVS 略优于 SVN(现在时)的说法感到困惑。
CVS repos 更容易导入到 Git 这样的现代软件中,因为 Subversion 做了一件蠢事,它试图把所有东西都变成一个路径,所以你最终会发现标签有多个修订版本,而项目使用它们自己的概念,并不完全符合基于低级文件系统路径抽象的分支或标签。
是的:Subversion “悄悄 ”摆脱了……标签和分支!对于版本控制系统来说,这很不寻常。更不寻常的是:我花了一两年时间才意识到这一点,并在这里进行了描述:
https://en.wikipedia.org/wiki/Apache_Subversion#Subversio…
是的,SVN 让分支变得非常容易(只需复制,很简单吧),但合并却很麻烦。有一次,他们增加了 “合并跟踪 ”功能,以追踪哪些修订是从哪里合并过来的,但对我来说效果并不好、
我想是莱纳斯(Linus)在一次关于 Git 的讨论中指出,修订控制系统真正需要擅长的是合并,而 SVN 在这一点上做得很糟糕。
只有 git-svn 才能让我使用它。这样,我就可以拥有本地分支,而无需处理服务器。
我第一次学习 git 是用……git-svn:纯粹是因为我对 SVN 感到厌烦(不仅仅是因为没有分支)。