Rust允许分发闭源库吗?



我是新手,刚看完那本书。我试图理解rust中的所有东西是否都必须以源代码的形式分发,比如golang。这可能不是准确的提问方式,但这是我想知道的:

在C/c++时代,我可以将代码编译成一些库(xx)。A或xx.so),并将其与头文件一起分发给可以链接他们的代码的人,只要我们使用相同的编译器/操作系统/平台。现在使用Rust时,每个依赖项都是从crate中提取的。IO的源代码和本地构建和链接的形式。然而,如果出于某种原因,我不想发布源代码,但只有目标平台的库,这可以通过操纵工具链来完成吗?我认为这是不可能的,因为Rust的一些固有设计。例如,我可以在rust库中编写如下代码:

pub trait Summary {
// snip
}
fn returns_summarizable() -> impl Summary {
// snip
}

如果依赖库的代码调用returns_summarizable,那么编译器必须能够确定返回值的大小,以便在堆栈上为结果变量分配空间。这意味着尽管代码只关心返回实现Summarytrait的类型但编译器实际上必须知道它返回的具体类型. 如果没有库的源代码,这是不可能做到的,甚至没有一些类似的头文件,因为头文件(如果存在)应该只包含trait声明,而不是函数可能返回的任何内部类型。

我对它很好奇,不是因为我想在没有源代码的情况下分发箱子,而是想到我,借用检查器和编译器从不查看函数或方法的主体,它被调用,但只有签名。当函数如上所述返回impl xxx时,情况似乎并非如此,因为Rust必须查看实际返回的实现类型以确定返回类型的大小,对吧?

这里的主要问题是Rust ABI还不稳定。因此很难正确地分发Rust二进制文件。因为它们只能在您编译它们时使用的同一编译器中工作。即使是一个小的版本变化也可能破坏它们。

现在安全的赌注是通过在Cargo.toml

中设置板条箱类型来分发staticlib和/或cdylib
[lib]
crate-type = ["staticlib", "cdylib"]

将分别生成一个静态库文件*.a和一个动态库文件*.so/*.dylib/*.dll,参见参考文章链接。

缺点是它使用了FFI接口,结果你失去了一些rust的表现力,只能使用C兼容的类型,这就排除了泛型之类的东西。

如果你不介意破坏/重建每个Rust小版本,你可以使用Rustdylib:

[lib]
crate-type = ["dylib"]

将在Linux上生成lib<your_crate_name>.so。然后,您可以告诉cargo用构建脚本或类似的东西链接该文件:

fn main() {
println!("cargo:rustc-link-lib=dy_lib");
println!("cargo:rustc-link-search=/home/jona/projects/dy-lib/target/release");
}

您还必须在外部块中提供函数签名,要么直接在使用它的地方,要么在存根库中:

extern "Rust" {
fn your_exported_function(Vec<i8>, String) -> Result<(), &'static str>;
}

请注意,由于泛型是通过单态实现的,所以仍然不允许它们,但是您至少可以使用rust表达类型系统的其余部分,尽管extern块中的所有函数都隐式地使用unsafe

虽然您可以返回impl Summary,但似乎无法动态返回两种不同的类型,例如此代码无法编译。操场上:

use rand;
trait Summary {
fn get(&self) -> usize;
}
struct A(usize);
impl Summary for A { 
fn get(&self) -> usize { return self.0; } 
}
struct B(usize);
impl Summary for B { 
fn get(&self) -> usize { return self.0; } 
}
fn returns_summarizable() -> impl Summary {
if rand::random() {
return A(42);
} else {
return B(21);
}
}
fn main() {
println!("{}", returns_summarizable().get());
}
error[E0308]: mismatched types
--> src/main.rs:20:16
|
16 | fn returns_summarizable() -> impl Summary {
|                              ------------ expected `A` because of return type
...
20 |         return B(21);
|                ^^^^^ expected struct `A`, found struct `B`

如果两个分支都返回类型A,那么代码将随机打印4221。因此,只要它只返回一种类型,编译器就可以将二进制签名中的impl Summary替换为A的大小

最新更新