软件开发的世界既广阔又多样,都充满了多种语言,每种都具有独特的优势。在最近获得了重大牵引力的较新的人中,是生锈的。 Go以简单性和速度而闻名,受到开发人员创建可扩展和高效软件的喜爱。另一方面,Rust在不牺牲表现的情况下专注于记忆安全,提供了大量的控制和可预测性。
自然出现的问题是:这两种强大的语言可以在一个应用程序中共存吗?我们可以在减轻他们的弱点的同时利用两者的优势吗?令人兴奋的答案是肯定的。这篇博客文章致力于解释如何实现。
Go和Rust的交汇处
去生锈,虽然截然不同,但熟练程度重叠。两者都是具有低级功能的系统级别语言,非常适合构建Web服务器,使用数据库或创建功能强大的API等任务。但是他们每个人都在不同的情况下发光。 GO非常适合快速开发和直截了当的并发性,非常适合高性能网络服务器。同时,在处理需要强大的计算资源或需要严格的内存安全的复杂逻辑时,生锈也会发光。
但是,一个人如何利用两者在一个项目中的力量?这是外国功能接口(FFI)发挥作用的地方。
外国功能接口(FFI)
外国功能接口(FFI)是一种允许编程语言与用不同语言编写的代码互操作的机制。它提供了一种用一种语言编写的程序来调用函数或使用用另一种语言定义的数据结构的方法。 Rust提供了有据可查的FFI,它允许其与C等其他语言进行交互,并扩展为
。FFI的主要目的是启用不同编程语言之间的无缝集成,使开发人员能够利用其代码库中用不同语言编写的现有库,框架或组件。
语言互操作性:FFI提供了一个标准化的接口,该接口定义了如何调用函数或在语言边界上使用数据。它可以在不同的编程语言(例如C/C ++,Python,Java或Rust)之间进行通信和数据交换。
函数调用:FFI允许您调用与代码不同语言编写的函数。它通常涉及提供函数原型或绑定来描述功能签名,参数类型和返回值。 FFI机制处理语言之间的编组数据的低级细节。
数据交换:除了功能调用外,FFI还允许您在语言之间交换数据。这包括在语言边界上传递数据结构,原始类型,数组或复杂对象。 FFI处理数据表示的转换,以确保语言之间的兼容性。
抽象水平:根据FFI实现,提供的抽象水平可能会有所不同。一些FFI提供了低级接口,需要手动内存管理并与外语的运行时直接互动。其他人则提供了更高级别的抽象来使内存管理自动化并与主机语言提供更惯用的集成。
FFI实现:不同的编程语言具有自己的FFI实现或库。例如,在C/C ++中,您可以使用诸如python中的“ extern”声明,标头文件或库中的诸如``ctypes''之类的库,“ jni”(jni''(Java本机界面)或python中的“ cffi” C代码。
绩效注意事项:使用FFI时,重要的是要考虑性能含义。跨语言边界可以由于数据编写,上下文切换或其他因素而引入开销。优化诸如最大程度地减少FFI呼叫的数量或使用诸如即时汇编之类的技术可以帮助缓解性能问题。
FFI被广泛用于利用现有的代码库,访问系统库或通过低级语言编写关键性能代码来提高性能。它们通过启用来自不同语言的软件组件来无缝地工作来提供灵活性和扩展性。
这是创建Rust库并将其与GO应用程序集成的一般分步指南。
步骤1:编写Rust库
首先,我们将创建一个执行内存密集操作的Rust库。
#[no_mangle]
pub extern "C" fn process_data(input: *const c_char) -> *mut c_char {
// Process the data
// Remember to ensure memory safety!
let output = /* result of computation */;
CString::new(output).unwrap().into_raw()
}
在这里,#[no_mangle]
属性至关重要,因为它告诉rust在编译过程中不要mangle函数名称,允许从我们的GO代码调用它。
步骤2:建造锈库
我们将Rust Code编译到共享库(linux的.so
文件或Windows的.dll
文件)。
$ rustc --crate-type cdylib -o output.so input.rs
步骤3:从Go中调用Rust
使用共享库,我们现在可以使用cgo
工具来调用我们的Rust函数。
/*
#cgo LDFLAGS: -L. -loutput
#include <stdlib.h>
extern char* process_data(char* input);
*/
import "C"
import "unsafe"
func ProcessData(input string) string {
c_input := C.CString(input)
defer C.free(unsafe.Pointer(c_input))
c_output := C.process_data(c_input)
defer C.free(unsafe.Pointer(c_output))
return C.GoString(c_output)
}
在GO代码中,C.CString
用于将GO字符串转换为C字符串(null终止的字符数组)。完成之后,我们使用C.free
来处理内存,以防止泄漏。
尽管这种组合可以为项目带来可观的好处,但也有要考虑的要点。越过语言边界时会有绩效影响。由于额外的开销,通过FFI的功能调用可以慢慢。因此,将跨越该边界的呼叫数量最小化是一个很好的策略。例如,如果您在每次迭代需要生锈功能的地方都有一个循环,则最好将循环移至Rust并进行单个FFI调用。
此外,两种语言之间的错误处理可能会变得棘手。 Rust的结果类型不会直接转化。因此,您需要实施一种机制,以将生锈错误转化为可以理解并正确处理的格式。
通过启用GO和Rust之间的沟通,我们可以构建应用程序,以利用GO的简单性和快速发展,同时受益于Rust的记忆安全和对系统资源的控制。这就像两全其美。
但是,这种合作带有其自身的挑战,包括函数呼叫开销和错误处理,在构建应用程序时必须考虑这些挑战。但是奖励可以是巨大的,导致应用程序更快,更安全,更有效。
在单个应用程序中,Go and Rust的融合展示了现代软件开发的功能和多功能性 - 能够为工作的每个部分选择合适的工具,以及使这些部分和谐地工作的手段。这证明了不断发展且令人兴奋的编程世界。