gpt4 book ai didi

configuration - 您可以使用 Cargo 共享依赖树上的 `cfg!` 吗?

转载 作者:行者123 更新时间:2023-11-29 08:08:34 25 4
gpt4 key购买 nike

我有一个 Rust 库,它有一些“后端”形式的平台抽象。该库使用 build.rs 进行一些平台检查,并根据可以构建的后端设置一些编译时配置变量。然后在代码中,后端代码是这样保护的:

#[cfg(backend1)]
struct Backend1 { ... }
#[cfg(backend2)]
struct Backend2 { ... }
...

这个库的使用者想要实例化一个适合当前平台的后端。理想情况下,您会执行以下操作:

fn get_backend() -> Box<Backend> {
#[cfg(backend1)]
return mylib::backends::Backend1::new(...);
#[cfg(backend2)]
return mylib::backends::Backend2::new(...);
...
}

但是,mylib 中的配置变量不会共享给消费者,因此 #[cfg(backend1)] 不会按预期工作。

有没有一种方法可以实现预期的行为,而不需要构建图书馆消费者的人进行人工干预?我不希望用户必须手动传递可用后端列表。看来这应该是自动化的。

请注意,未内置于 mylib 的后端结构完全不存在,这意味着消费者无法引用它们。消费者需要使用条件编译来确保仅引用 mylib 中内置的后端。

任何给定平台可能有多个后端,在这种情况下,消费者应该能够选择哪一个。

最佳答案

您无权从外部访问图书馆的配置。

您永远无法从消费者代码中获知后端的具体类型,因此您必须想出一些机制来构建它们,同时考虑到每个构造函数的不同需求。

这里的基本思想是引入上下文,类似于您可能在面向对象语言中使用的依赖注入(inject)上下文。上下文包含构造函数可能需要的值。

要创建特征对象,您需要一个特征:

pub trait Backend {
// all the common stuff for backends
}

用于构建后端的特征,以及用于保存这些后端所需的所有可能配置变量的结构。这不能与 Backend 具有相同的特征,因为 new 方法阻止将其制成对象。大多数变量是可选的,因为并非所有后端都需要它们:

pub trait BackendContstruct {
fn new(ctx: &BackendContext) -> Result<Box<Backend>, BackendError>;
}

pub struct BackendContext<'a> {
var_1: Option<&'a str>,
var_2: Option<&'a str>,
another: Option<bool>,
// etc
}

如果您提供了错误的变量,那么您需要返回一个错误。不幸的是,动态构建意味着错误是运行时的,而不是编译时的:

pub struct BackendError(String);

每个后端的可用性取决于平台支持。所以让他们的定义依赖于平台:

#[cfg(platform1)]
mod backend1 {
pub struct Backend1;
impl ::Backend for Backend1 {}
impl ::BackendContstruct for Backend1 {
fn new(ctx: &::BackendContext) -> Result<Box<::Backend>, ::BackendError> {
if ctx.var_1.is_none() {
Err(::BackendError("Backend1 requires val_1 to initialize".to_string()))
} else {
Ok(Box::new(Backend1 {}))
}
}
}
}

#[cfg(platform1)]
#[cfg(platform2)]
mod backend2 {
pub struct Backend2;
impl ::Backend for Backend2 {}
impl ::BackendContstruct for Backend2 {
fn new(ctx: &::BackendContext) -> Result<Box<::Backend>, ::BackendError> {
Ok(Box::new(Backend2 {}))
}
}
}

没有一个具体类型是公开的,而且任何一个都可能不存在。因此,提供一个枚举,以便消费者可以指定他们想要的后端:

pub enum BackendType {
// these names are available in all configurations
Default, Backend1, Backend2, Backend3
}

还有一个构建后端的函数。请求不支持的后端或遗漏上下文中所需的变量将是一个Err。应该鼓励消费者使用 Default 变体,它应该在任何平台上都有一个有效的后端:

pub fn create_backend(backend: BackendType, ctx: &BackendContext) -> Result<Box<Backend>, BackendError> {
match backend {
#[cfg(platform1)]
#[cfg(platform2)]
BackendType::Default => Backend2::new(ctx),
#[cfg(platform1)]
BackendType::Backend1 => Backend1::new(ctx),
#[cfg(platform1)]
#[cfg(platform2)]
BackendType::Backend2 => Backend2::new(ctx),
_ => Err(BackendError("Backend not available".to_string()))
}
}

关于configuration - 您可以使用 Cargo 共享依赖树上的 `cfg!` 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50447703/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com