V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ciaoSora
V2EX  ›  编程

浅分享下个人喜欢 Rust 超过其他语言的原因

  •  1
     
  •   ciaoSora · 92 天前 · 2472 次点击
    这是一个创建于 92 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本人技术实力一般,所以自认为更能从大众程序员的视角对比一下 Rust 和其他语言,以及浅浅聊下为什么更喜欢 Rust 。

    用过很多语言了,包括 C, C++, Go, JS, TS, C#。最近几个月一直在用 Rust 工作,用下来的整体体验就是:其他语言用着不爽的地方,在 Rust 这边总是能非常自然地解决。以下是详细版。

    内存管理

    大家用 Rust 基本都是奔着这个来的,这方面确实是吊打 C/C++。C++ 的智能指针是在运行时保证安全的,跟 Rust 在编译期保证安全没法比。而且,在 Rust 里也可以用 unsafe 来手动管理内存,非常灵活。跟其他带 GC 的语言比就见仁见智了。

    工具链

    配套工具可以说是一应俱全,都是由官方提供,令我这种想开箱即用的程序员体验非常好。包管理器有 Cargo ,代码格式化工具有 rustfmt ,linter 有 clippy ,还有 VSCode 插件。C/C++ 就不说了,非常难评。Go 的工具链也不错,包管理、格式化工具之类的也都有(但是不能不吐槽早年间的奇葩设计 GOPATH),感觉能打个平手。JS/TS 生态感觉会有些混乱,包管理器就有好几个了,然后 linter 不自带,虽然配置好了之后用起来也很舒服,但是自己配置会有些烦,不像 Rust 开箱即用。C# 就更复杂了,不过考虑到这是微软那一套东西,似乎就该是臃肿又难用。

    错误处理

    Rust 采用返回 Result<T, E> 的方式,Go 采用返回多个值(其中有一个表示是否有错),其他都是 throw-try-catch 。

    个人很不喜欢 throw-try-catch 的方式,首先 try block 自己是一个 scope ,我不得不在 try block 的外面先声明变量,然后再在 try block 里给变量初始化/赋值,很不喜欢这样子。其次就是,比如 JS/TS ,就不会强制要求定义函数时要同时定义该函数可能抛出的异常的类型,这就让我开发时感觉如履薄冰,我永远不知道会有什么神奇的异常被抛出。

    Go 的 if err != nil 已经被很多人诟病过了,虽然我认为这样子做已经比 throw-try-catch 好一些了——因为强制程序员错误处理或者显式表明不想处理——但是在函数的返回值里,当出错时,表示错误的返回值有意义而其他返回值无意义;当顺利执行时,表示错误的返回值无意义而其他值有意义,这个设计对我来说实在是槽点太大了。

    Rust 则让函数返回一个 Result<T, E> 的 enum value ,这就非常优雅,一个返回值承载了顺利和出错这两种情况,而且跟 Go 一样,这强制程序员处理错误或者显示表明不想处理。除此以外,假设函数 fun1 调用了 fun2fun1 的返回值类型为 Result<T1, E1>fun2 的返回值类型为 Result<T2, E2>, 当 E2 类型能「转换」(Into trait )成 E1 类型或者 E2E1 相同时,若 fun1 想要直接返回 fun2 返回的错误时,有语法糖,使得代码可读性提高很多。

    元编程

    Rust 支持简单的用 macro_rules! 定义的宏,更复杂的宏(过程宏,procedural macro )可以用标准 Rust 语言实现,只需要写一个接收 token 输入,并且输出 token 的函数即可。这些宏在编译期间会被编译器执行并展开,然后再进行后续编译。

    C/C++ 的宏只支持字符串处理,与 Rust 能在 token 级别处理形成鲜明对比。C++ 还支持 template ,不过这方面 Rust 更加强大(配合上 Rust 的强大的宏)。

    TS 的 decorator 确实也算元编程,但是 decorator 的内容是在运行时执行的,损失了一点点运行效率。C# 印象中也是运行时(错了的话麻烦纠正我,谢谢)。

    (不过 TS 的编译器类型推断确实很骚,能在编译时期用函数式编程的方法做归并排序…… 虽然还是被 Rust 吊打,毕竟 Rust 的宏就是一个标准 Rust 函数,里面可以为所欲为)

    Rust 除了宏,还有 trait 可以做元编程,不过暂时没觉得非常惊艳。(而且印象中 trait 和 async 的配合相对会局限一些,所以感觉更不值得吹嘘这一点)

    代码结构和可见性

    Rust 里,各个 module 组成了树形结构,官方也确实提倡把 module 结构映射到操作系统的文件系统里,因此很清晰。每个 item (即 module, function, struct, enum, trait 等)都有可见性( public or private?),子 module 默认可以访问父 module 的 item 。其他情况下,module A 可访问 module B 的 item ,当且仅当 A 和 B 的最近公共祖先到 B 的路径上,每个 module 都是 public ,并且欲访问的 item 也是 public 的。

    我很喜欢的一点就是,父 module 里的 private items ,在子 module 里是可以访问的,这就好像我有一个很大的功能模块,这个功能模块有若干子模块,我可以在这个功能模块里定义一些不想暴露给外界,但是子模块里又需要使用的东西。其他语言很难做到这样,他们如果想让子模块访问父模块的东西,就只好让父模块里的东西 public ,但是这样子就不仅仅只暴露给了子模块,还暴露给了外界。

    C/C++ 里,可以透过头文件来控制可见性,也可以用 public, protected, private 来控制。但是由于不同功能模块之间没有语言级别支持的父子关系(namespace 有父子关系,但是父子关系没什么用?),就无法实现我喜欢 Rust 的那一点。不过 C++ 有友元,可以更精细的控制…… 但是我还是喜欢 Rust 这种很自然的默认设置。JS/TS 和 C# 也差不多。

    Go 真的槽点太大了,靠 item 的首字母是否大写来决定可见性…… 个人反正是很讨厌。

    内置数据类型

    C++ 的 int, long, long long, short 分别是几个字节?跟具体的 CPU 相关,并不是定死的。

    Go 里有 int8, uint64 等类型,直接写明这个类型占几字节,可惜除此以外,Go 里还有 intuint,又是跟 CPU 相关的。Rust 里,每个整数类型都写明了占几字节,而且没有跟 CPU 相关的整数类型。这种强制程序员指明数据长度的设计,我很喜欢,这样子大大提高跨平台的可能性。同时,Rust 里也有 引用/指针 类型,这个确实是跟 CPU 相关的(也不得不跟 CPU 相关),但是至少 引用/指针 类型不是整数类型。

    总结

    个人粗浅的理解:Rust 披着高级语言的外衣,实际上高层、底层的事情都能做,是一门严谨的、高效的、语法现代化的、安全的编程语言。个人用了一段时间,真的感觉很舒服。开发时最喜欢干的事情就是写宏(没错我就是宏孩儿)来解耦,写完了一个宏之后,用起来的感觉是真的爽,一行简单的代码,生成了一堆复杂的代码,很有成就感,代码维护成本也大大降低(不过宏维护起来确实也难一点)。

    欢迎大家补充以及指正我的错误!!

    第 1 条附言  ·  92 天前
    写的时候忘记 Rust 有 `usize` 和 `isize` 类型了,这个跟 CPU 相关,我谢罪 orz
    27 条回复    2024-09-04 21:35:19 +08:00
    querysecret
        1
    querysecret  
       92 天前
    哈哈,没人回复,前几天有一个 GO 的就 100 多楼,说明 Rust 不招人喜爱
    ciaoSora
        2
    ciaoSora  
    OP
       92 天前
    @querysecret 我刚发出来没两分钟的帖子🤣 顺便问下 Go 的 100 多楼的贴在哪,我去对线
    zsj1029
        3
    zsj1029  
       92 天前
    支持一下,js 配合 bun 编译写后端挺爽,想试试 1m 内存运行的 rust ,学习一下
    panlatent
        4
    panlatent  
       92 天前
    标题改成《 Rust 超过其他语言的原因》 发到编程语言的节点 :D
    ciaoSora
        5
    ciaoSora  
    OP
       92 天前
    @panlatent 移动到「编程」节点了,不敢这样改标题,分分钟被喷烂 XD
    zw1196650986
        6
    zw1196650986  
       92 天前
    最近也在学 rust,但是这玩意不太好找工作,只能靠兴趣学习
    ciaoSora
        7
    ciaoSora  
    OP
       92 天前
    @zw1196650986 区块链领域有很多用 Rust ,Solana 的智能合约最主流用 Rust 写,还有些钱包也用 Rust 写。互联网领域感觉主要是既有的框架过于成熟了,各个团队、公司肯定是懒得迁移,而且也不敢用新东西。我自己用 Rust 写过后端服务和前端页面,感觉刚开始会有一点不适应,写一会感觉就还好了~
    ciaoSora
        8
    ciaoSora  
    OP
       92 天前
    @zw1196650986 哦,一些量化系统也有些开始用 Rust 了,可能 Rust 确实比 C++ 用起来安心一点吧
    minami
        9
    minami  
       92 天前
    别的不谈,整体看下来感觉都是主观喜好,特别是一看 C++也用的不是很熟的样子。。。不过素质已经吊打大部分 Rust 教徒了,今天就不喷编程原神了
    povsister
        10
    povsister  
       92 天前
    因为 rust 还是相对小众一些,不懂的人来指指点点很容易反被喷,go 就不一样了,毕竟是强类型 Python (狗头)

    自己目前用 rust 还是拿来写一些中间件或者资源/性能要求的小组件,业务/web 的话用起来还是没有 go 顺手
    AllenTsui
        11
    AllenTsui  
       92 天前
    OP 再学习一下 zig ,听说 zig 的宏编程比 rust 惊艳。
    hingle
        12
    hingle  
       92 天前   ❤️ 1
    Rust 的 isize 和 usize 是固定大小?
    ciaoSora
        13
    ciaoSora  
    OP
       92 天前
    @hingle 草,有道理……
    virusdefender
        14
    virusdefender  
       92 天前
    rust package mod 之类的概念感觉有点过于复杂了
    cmdOptionKana
        15
    cmdOptionKana  
       92 天前
    写得很好,完全可以作为一篇优秀的博客文章啊
    Pierro
        16
    Pierro  
       92 天前
    标题改成 java 这么烂为什么还有这么多人用 为什么不换成 rust
    IvanLi127
        17
    IvanLi127  
       92 天前
    @querysecret 这下路过不得不回复一下了。Rust 万岁,人气+1
    Bazingal
        18
    Bazingal  
       92 天前   ❤️ 1
    C#的 Source Generator 是编译时元编程
    csys
        19
    csys  
       92 天前
    在语言特性上,go 是不配和 rust 比的,比如你说的 Result<T, E>和 trait ,以及非常好用的模式匹配,rust 吸收了很多优秀的编程语言特性,拿 golang 比,go 简直就像是原始人手里的棒子

    我的感受是 rust 的最大问题在于繁琐,不只是写起来繁琐,读起来也挺难受的

    另外就是 rust 和 go 的通病,既不够 oo ,又不够函数式,go 就当它摆了,但是 rust 有这么先进高级的语言特性,结果只能写出和 go 一个模样的代码,实在是难以接受,有拿数控机床造砖块的感觉
    wangweei
        20
    wangweei  
       92 天前 via iPhone
    C++也可以使用 uint32 之类的固定长度基本数据类型
    wlingxiao
        21
    wlingxiao  
       91 天前 via Android
    这不学一手 Scala (狗头
    yoiteshaw
        22
    yoiteshaw  
       91 天前
    写得不错,rust 带给程序员的安全性,是很棒的。但是每次都要 unwrap 让我很烦,不够优雅。(可能是我掌握不到位
    StarsunYzL
        23
    StarsunYzL  
       91 天前
    工具链
    C++除了没有靠谱的包管理器外哪里差了

    错误处理
    C++23 有 std::expected<T, E>,C++11 可以用微软 VC 团队成员开源的 tl::expected<T, E>实现

    元编程
    先把 C++的 template 玩明白了再夸其他的

    代码结构和可见性
    过于主观

    内置数据类型
    C++几百年前就有了固定长度的(u)int8_t/16/32/64 类型
    NPC666
        24
    NPC666  
       91 天前 via Android
    其实 C++上手难度比 Rust 高多了,Rust 如果不和 C/C++的 binding 交互,只写 rust safe 的话,开发体验是非常棒的。
    liuran
        25
    liuran  
       91 天前
    还有 rust 放弃了 C++和其他过去的面向对象语言的 Class ,而是采取 trait ,这一点和 go 的鸭子类型类似,我都感觉是非常好的实现方式,从个人体验上来说,灵活性和使用体验都比 Class 好很多。

    但是工作中是写 C++的,个人项目目前还没有什么好的要用 rust 写的项目,于是总是在入门 rust 之后忘掉,下次再入门再忘掉的循环中度过。。。
    bunny189
        26
    bunny189  
       74 天前 via iPhone
    老哥,能问问你是怎么入门的吗?先看圣经还是其他资料呀?
    ciaoSora
        27
    ciaoSora  
    OP
       72 天前
    @bunny189 我是看了 rust 官网推荐的 Rust Book 和 Rust by example ,入门之后就可以直接看 Rust Reference 和 Rust Nomicon 了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2646 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 11:20 · PVG 19:20 · LAX 03:20 · JFK 06:20
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.