C++和Rust_新项目用 Rust 还是 Go ?

如果你用 Rust 语言或 Go 语言编写过代码,就会发现它们之间有些相似之处和不同之处。这两种语言的设计目标有重叠的部分,但也有很多差异。正如我们知道的,该如何选择语言取决于要解决的问题。
很幸运,我们找到了一位对这两种语言都有着丰富经验的工程师 — Damien Stanton,并与他进行了一次交流。他从 2014 年开始接触到 Rust 和 Go 语言,当时他从事的某个项目需要能够编译静态二进制文件。由于 Go 和 Rust 语言均具备这样的能力,因此他深入研究了每种方法的优缺点。
虽然在那个项目上他的大部分专业工作都是以 Go 语言实现的,但是他对于 Rust 社区解决各种问题抱有很大的兴趣,比如错误处理,泛型和并发。
于是他花了大量时间研究这两种语言,而且最终他将研究成果提交给了 Go 语言社区——“Go 程序员看 Rust”。
下面就是我们与 Damien 的交流内容,包括:两种语言的的差异、相似之处、以及二者之间的一些争议点。

你觉得它们(Rust 和 Go)有哪些相似之处?设计目标的重叠指什么?

这两种语言以及背后的生态都在尝试解决许多相同或类似的问题。
它们都是介于偏底层的系统语言(就像 C 或 C++ 语言)和基于运行时的语言(比如 Java 或 Python)之间。但它们都更适用于系统编程领域。
易于部署/分发和方便交叉编译是我认为它们都做得很好的点,同时内存安全对两种语言也很重要。它们都天然具备解决高性能或高并发的问题的能力,而这也是使用传统的 Python 或 Java 不容易做到的。
对于内存管理方面,二者使用了完全不同的方式。同时稳固、可靠、易于维护和升级二者做得都很好。

2020年,很多社区或组织似乎都表达一种观点:同时使用这两种语言来解决不同的问题。那么你目前一般会如何选择?

  • 使用 Go:曾经有观点说“Go 语言是更友好的 C 语言”。但是就我的使用经验和观察来说,Go 可能是 Java 的更好继承者,因为它适合构建大型分布式系统。这也是你看到的像 Kubernetes 和 Docker 使用 Go 编写的原因。

  • 使用 Rust:更适用于对时间/空间要求苛刻的场景,比如微控制器。另一个重要场景就是 Web Assembly。在 Rust 社区中,构建编译成 Web Assembly 并跑在浏览器中工具非常多。于这一点我认为 Rust 更类似于C++。

如果需要在这两种语言之间做选择,那我认为更多是从编程经验和能力上考量,而非要解决的特定问题。
根据我在初创公司的经验,当时我们选择使用 Go,更多是因为它更容易上手,适合当时处于快速增长时期的团队。新的团队成员可以迅速开始产出。
但对于 Rust 而言就不是如此了。由于它独特的内存模型等特点,Rust 的学习曲线更陡峭,更难上手。当然我也发现那些具备 C++ 或 Java 背景的团队可能会选择它。

围绕 Rust 和 Go 的讨论很多,哪些点是更吸引你的?

泛型

在 Go 社区内部,最常见的抱怨就是范型的缺失。相比而言,Rust 具有强大的类型系统并支持泛型。它的设计哲学不同于 Go ,类型系统方面借鉴了 Haskell 和 C++。
GopherCon 2019 上,有一个关于范型的演讲提出了一种语法表达。我认为在 Go v2.0之前,肯定会看到对于范型的正式支持。(如果不得不猜测的话,大概在v1.16~v1.19之间)。

并发

在过去的六个月里,Rust 并发特性发生了很大变化。
Rust 和 Go 使用者之间的常见讨论就是并发。Go 具有 goroutine+channel 的并发模型,而 Rust 则具备更简单的多线程模型。类似于 C++ 或 Java,但是具有更好的线程间通信能力,比如 MPSC channel(非常类似于 Go channel)。
但是现在,Rust 朝 Future 模型发展,其实就是我们已经熟悉的 async/await 模型,以同步的方式和思维编写代码,但以异步方式执行。
Rust 还有一个功能非常强大的宏(macro)系统,可以使编译器做很多工作,比如生成代码,除此之外还有更多可以细粒度控制的细节。所以这意味着 Rust 的学习挑战很大。

Go 的构建工具

长期以来,构建工具一直是 Go 程序员的抱怨。直到最近我们才切换到了 Go mod,整体来说,Go 的相关工具够用但还不够强大。比如开发人员希望为他们的编辑器提供稳定的语言服务器(languange server,可参考 lsp 协议),易于使用的依赖管理解决方案以及良好的自动文档和发布工具等等。另一方面,Rust 的 cargo 相当出色,完善的文档,并且可以轻松扩展子命令。

Fuschia:Google 的 Android 后继产品

关于 Go 与 Rust 的一个有趣的用例是 Google 的新软件 Fuschia,该软件最终将取代Android。

Google 主要使用 C++ 构建该系统,但是其中有一些来自 ChromeOS 的 Rust 组件,其中大量使用 Rust。为了确定用户如何为 Fuschia 编写程序(即,用户级程序而不是内核组件),他们研究了五种语言,包括 Go 和 Rust。在这种情况下,Go 的内存管理开销会导致过多的延迟。这是内存管理语言的普遍问题,最终只能用 C++ 或 Rust 编写面向用户的代码。

发布周期

这两种语言有非常不同的发布方式和发布周期:

  • Go 的发布周期:有些规律但依然是一个黑匣子;核心团队之外没有人真正知道何时发布新版本,而且通常是通过一篇博客就宣布发布了。在 Gerrit/GitHub 上以 issue 形式存在的文档很多,不够完善和透明。

  • Rust 的发布周期:更加透明;核心团队在 YouTube 上记录了所有团队会议,你可以实实在在看到他们的讨论。特别是看到语言背后的设计思考和决策很有趣。Rust 具有明确的滚动发布周期,具有良好的稳定/测试版/尝鲜版说明。

我个人认为,从长远来看,使所有内容完全开放和透明可能更好。
总而言之,很有趣的是看到它们的组织风格如此不同,但是两种语言都取得了巨大的成功。Go 和 Rust 都是很棒的语言,我希望两个社区的新手和资深程序员可以聚在一起,互相学习!

原文地址:https://dmv.myhatchpad.com/insight/choosing-between-rust-or-go/

参考阅读:

  • dubbo-go 1.4.0 版本发布,支持 K8s 注册中心、rest 协议

  • 从lstio的角度谈微服务的一些误区

  • Go语言如何实现stop the world?

  • Swift程序员对Rust印象:内存管理

  • JDK 14发布,空指针错误改进正式落地

  • 如何快速定位 Redis 热 key?

本文由高可用架构翻译。技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿。

高可用架构

改变互联网的构建方式

df075e7bd7dfc48fa7bd3b6f0b4f08b1.png长按二维码 关注「高可用架构」公众号

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++ 中的 `shared_ptr` 和 Rust 中的 `Rc`、`Arc` 都是智能指针,可以用于内存管理,但是由于语言不同,它们在底层实现上有很大的差异,因此在 C++Rust 之间交互时需要特别注意。 要在 C++Rust 之间交互 `shared_ptr`,可以使用 `cxx` 库进行绑定。具体来说,我们需要在 C++ 中将 `shared_ptr` 转换为 `void*`,然后将其传递给 RustRust 再将 `void*` 转换为 `Rc` 或者 `Arc`。 以下是一个简单的例子,演示了如何在 C++Rust 之间交互 `shared_ptr`: C++ 代码: ```cpp #include <memory> #include <iostream> #include <cxxbridge/cxx.h> struct Foo { int x; Foo(int x) : x(x) {} void hello() { std::cout << "Hello from Foo(" << x << ")!\n"; } }; std::shared_ptr<Foo> make_foo(int x) { return std::make_shared<Foo>(x); } void use_foo(std::shared_ptr<Foo> ptr) { ptr->hello(); } void* get_raw_ptr(std::shared_ptr<Foo> ptr) { return (void*) ptr.get(); } CXX_EXPORT void cxxbridge_demo_run() { auto foo = make_foo(42); use_foo(foo); void* raw_ptr = get_raw_ptr(foo); // 将 void* 指针传递给 Rust rust_use_shared_ptr(raw_ptr); } ``` Rust 代码: ```rust use std::rc::Rc; use std::cell::RefCell; use std::ffi::c_void; struct Foo { x: i32, } impl Foo { fn new(x: i32) -> Self { Self { x } } fn hello(&self) { println!("Hello from Foo({})!", self.x); } } // 用于从 void* 指针构造 Rc 智能指针 fn from_raw_ptr<T>(ptr: *mut c_void) -> Rc<T> { unsafe { Rc::from_raw(ptr as *const T as *mut T) } } // 用于从 Rc 智能指针构造 void* 指针 fn to_raw_ptr<T>(rc: Rc<T>) -> *mut c_void { Rc::into_raw(rc) as *mut c_void } // 用于接收 C++ 中传递的 shared_ptr 指针 #[no_mangle] extern "C" fn rust_use_shared_ptr(ptr: *mut c_void) { let rc = from_raw_ptr::<Foo>(ptr); rc.hello(); } fn main() { let foo = Rc::new(Foo::new(42)); let ptr = to_raw_ptr(foo.clone()); unsafe { // 将 Rc 智能指针指向的内存传递给 C++ cpp_use_shared_ptr(ptr); } } // 用于调用 C++ 中的函数,并传递 Rc 智能指针指向的内存 #[link(name = "cxxbridge_demo")] extern "C" { fn cpp_use_shared_ptr(ptr: *mut c_void); } ``` 在这个例子中,我们定义了一个 `Foo` 类和两个函数 `make_foo` 和 `use_foo`,它们分别用于创建 `shared_ptr` 类型的对象和使用 `shared_ptr` 类型的对象。我们还定义了一个 `get_raw_ptr` 函数,它可以将 `shared_ptr` 转换为 `void*`,方便在 C++Rust 之间传递。 在 Rust 代码中,我们定义了一个 `Foo` 结构体,并实现了 `from_raw_ptr` 和 `to_raw_ptr` 函数,用于将 `void*` 指针和 `Rc` 智能指针进行转换。在 `main` 函数中,我们创建一个 `Rc` 智能指针,然后将其指向的内存传递给 C++ 中的 `cpp_use_shared_ptr` 函数。在 `rust_use_shared_ptr` 函数中,我们将 `void*` 指针转换为 `Rc` 智能指针,并使用其调用 `hello` 方法。 最后,我们需要使用 `cxx` 将 C++Rust 代码进行绑定。具体来说,我们需要在 C++ 代码中使用 `CXX_EXPORT` 宏将 `cxxbridge_demo_run` 函数导出为 C 风格的函数,然后在 Rust 代码中使用 `extern "C"` 关键字声明该函数。同时,我们还需要在 Rust 代码中使用 `#[link(name = "cxxbridge_demo")]` 注解来链接 C++ 库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值