Rust 让人奔溃的那些特性!

作者 | William Woodruff

译者 | Arvin,责编 | 屠敏

头图 | CSDN 下载自东方 IC

出品 | CSDN(ID:CSDNnews)

以下为译文:

五年前,我写了一篇文章论述了我当时(至今)最喜欢的脚本语言Ruby让我讨厌的地方(https://blog.yossarian.net/2015/09/28/Five-Things-I-Hate-About-Ruby)。

今天,我将对我目前最喜欢的编译型语言:Rust做同样的事情。

就像最初写的关于Ruby的帖子一样,这些抱怨都是个人观点,反映了我目前对这个语言的最佳理解。就像写关于Ruby的文章一样,这篇也是出于对Rust的热爱而编写的。

闲话少说,让我们开始主题。

字符串地狱

我在开发过程中抱怨Rust中的字符串有两个大方向:

1.字符串类型之间的区别令人困惑

2.字符串类型之间进行转换的方法太多

字符串类型太多

我可以想到5种不同的方式来表示字符串[1],字符串视图或接受字符串形式的方法签名:

  • &str 用于表示借来的字符串

  • String用于表示自有的字符串

  • &OsStr 用于表示从操作系统借用的字符串

  • OsString 用于表示操作系统中自有的字符串

  • AsRef用于方法签名,表示一个廉价的&str引用

(我知道最后一个并不是真正的字符串类型,但是它经常出现在惯用的字符串处理代码中。)

作为Rust新手,这几种类型之间的区别非常令人困惑,也使得理解引用变得更加困难(为什么引用&String与&str不同?为什么我不能直接创建str?我从哪里得到&&str?)。

在字符串之间进行转换的方法太多

多种字符串类型和相关特征带来多种转换功能:

  • &str到String:在不考虑格式化转换或往返一个Vec或[u8]的情况下,至少存在这么多种,String::from(),to_string(),to_owned(),into()

  • String到&str:as_str(),as_ref(),Deref<Target=str>,&x[..]

  • 适用于OsStr和CStr的类似(可能有损)方法

这些转换中的大多数在性能上是等效的,Rust社区似乎对哪些是“正确的”存在分歧。

我最终习惯于根据上下文使用不同的字符串(例如into(),表示要将a &str转换为a,String以便可以将其返回,to_owned()表示稍后将拥有该字符串的所有权)。

标准库差距

Rust标准库存在一些空白,这些空白使用户空间编程的各个方面都很痛苦:

  • 当前没有获取用户主目录的方法。std::env::home_dir()被明确标记为已弃用,并且该文档鼓励用户使用第三方库dirs(但是该库已经不再维护,详见GitHub链接:https://blog.yossarian.net/2020/05/20/Things-I-hate-about-rust%20/l%20fn:2)[2]

  • 没有标准的扩展方式~。std::fs::canonicalize支持.和 ..,但不支持~。这其实和上面一点有所重复。

  • 无法通过系统shell调用命令。我知道system[3]命令有各种问题。我也同意它不应该是执行其他进程的默认接口,甚至应该被隔离以防止意外使用。但所有这些都没有改变这样一个事实--即这个命令偶尔会有用,在标准库中实现比最终开发人员直接使用sh-c更可靠。

没有标准的方法进行glob操作。似乎 glob第三方库是执行此操作的半官方方法。

这些都是公认的微小差距,所有这些都可以通过高质量的第三方库解决。但是它们会在开发过程中增加摩擦,鉴于Rust无摩擦的特性,摩擦是特别值得注意的。

 

特质(Traits)

我喜欢基于特质的组合。但我不喜欢下面这些东西:

  • 被告知我忘记了使用use std::io::Read或者use std::io::Write的方法,但其实原因是我正在调用的方法已经被作用域中的一些东西调用了。我知道为什么 Rust会这样做,不然就会出现编译器警告,但是我仍然感觉很奇怪,尤其是在未使用导入的情况下。

  • 为特质实现特质的语法。impl<T> for Trait for T where T: OtherTrait 虽然不算太糟糕,但它读起来不像impl Trait for OtherTrait那么自然。

  • 有时Rust编译器rustc需要我在静态函数(non-self)特质函数中添加where Self: Sized。我仍然不明白为什么有时需要这样做,而有时则不需要。但我相信这是有正当理由的。

 

无需扩展的安全索引

给定一个固定数组x = [T; N]和类型为U(U的约束为U::MAX < N)的索引变量i,通过x[i] 索引始终是安全的。尽管如此,Rust编译器rustc希望程序员能够明确将i扩展到usize:

fn main() {
    let lookup_table: [u8; 256] = [0_u8; 256];
    let index = 5_u8;
    println!("{}", lookup_table[index]);
}

失败结果:

error[E0277]: the type `[u8]` cannot be indexed by `u8`
 --> src/main.rs:4:20
  |
4 |     println!("{}", lookup_table[index]);
  |                    ^^^^^^^^^^^^^^^^^^^ slice indices are of type `usize` or ranges of `usize`
  |
  = help: the trait `std::slice::SliceIndex<[u8]>` is not implemented for `u8`
  = note: required because of the requirements on the impl of `std::ops::Index<u8>` for `[u8]`

虽然这可以理解,但要求程序员要么在索引计划中的任何位置使用as usize(冗长,并掩盖了索引背后为 u8的意图),要么将索引本身变成usize(也掩盖了意图,并使算术运算变得更容易超出界限),还是有些强求。

 

最后一点:cargo install 有时不能成功

我不知道这是不是一个真正的错误,但是由于我被它坑了几次,所以我将它加了进去。

cargo install显然不知道如何发现带后缀的软件包版本。例如,如果我发布myfakepackage为version 0.0.1-alpha.0,cargo install则会报告如下错误:

$ cargo install myfakepackage
error: could not find `myfakepackage` in registry `https://github.com/rust-lang/crates.io-index`

你必须明确传递—version参数:

$ cargo install myfakepackage --version 0.0.1-alpha.0

总结

我还有其他一些想讨论的内容(对于不支持特质的核心类型的别名,程序包生态系统的样式有点像JS / -y),但是我认为这样做有可能对我非常满意的语言太过消极。

五年过去了,我仍然喜欢Ruby,并且对Rust感到乐观。

备注说明:

1.没包括CString和&CStr,因为它们主要用于FFI上下文,并且可以理解为是不同的。↩

2.我知道在POSIX平台上可靠地获取用户的主目录实际上非常困难。但这并没有改变标准库应该尝试的事实。↩

3.恰当的例子:CLI经常公开钩点和回调,这些钩点和回调能够使用shell语法编写很有用

作者:William Woodruff,研究和工程实践者,开源软件参与者,软件库Homebrew软件开发成员,kbsecret的首席维护者

原文:https://blog.yossarian.net/2020/05/20/Things-I-hate-about-rust

本文为 CSDN 翻译,转载请注明来源出处。

【END】

6月2日20:00,CSDN 创始人&董事长、极客帮创投创始合伙人蒋涛携手全球顶级开源基金会主席、董事,聚焦中国开源现状,直面开发者在开源技术、商业上的难题,你绝不可错过的开源巅峰对谈!立即免费围观

更多精彩推荐
☞重磅!阿里巴巴开源首个边缘计算云原生项目 OpenYurt
☞腾讯面试题: 百度搜索为什么那么快? | 原力计划
☞都无代码了,还要程序员吗?
☞附代码 | OpenCV实现银行卡号识别,字符识别算法你知多少?
☞因为一个跨域请求,我差点丢了饭碗
☞区块链的 Layer 2 扩容(Scaling)是否兑现了其承诺?
你点的每个“在看”,我都认真当成了喜欢
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页