CSDN|Rust 让人奔溃的那些特性!



CSDN|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?) 。
【CSDN|Rust 让人奔溃的那些特性!】 在字符串之间进行转换的方法太多
多种字符串类型和相关特征带来多种转换功能:
  • &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 myfakepackageerror: 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 翻译 , 转载请注明来源出处 。