假设你有这个Config结构体,它在程序启动时加载一次,然后在整个生命周期中都是不可变的。
问题是,Config需要被程序的许多部分访问:
struct UsersService {
config: Config,
}
struct OrdersService {
config: Config,
}
// ...
let config = config::load()?;
let users_service = UsersService::new(config.clone());
let orders_service = OrdersService::new(config.clone());
在上面的代码中,Config被嵌入到两个结构体中,这可能不是理想的,因为这两个结构体将随着Config的大小而增长,而它们可能只需要访问1或2个字段。
一个好的选择是使用智能指针:Rc或Arc,这样我们就可以共享Config的引用。因为我们的程序是多线程的(就像现在的大多数程序一样),我们将使用Arc指针,这样我们的结构就可以在线程之间发送:
struct UsersService {
config: Arc<Config>,
}
struct OrdersService {
config: Arc<Config>,
}
// ...
let config = Arc::new(config::load()?);
let users_service = UsersService::new(config.clone());
let orders_service = OrdersService::new(config.clone());
这里,UsersService和OrdersService只嵌入了一个Arc指针,这只增加了8个字节。
是否能做得更好呢?对于在程序的整个生命周期中都是不可变的数据,最好使用&'static引用。
但是如何创建&'static引用的Config,在运行时加载?
请使用Box::leak,它在堆上分配内部结构体(这里是Config),并将引用“泄漏”到'static的生命周期。
struct UsersService {
config: Arc<Config>,
}
struct OrdersService {
config: Arc<Config>,
}
// ...
let config = Arc::new(config::load()?);
let users_service = UsersService::new(config.clone());
let orders_service = OrdersService::new(config.clone());
代码仍然与我们的原始代码非常相似,但是现在我们的UsersService和OrdersService只嵌入一个指针大小的引用,并且运行时开销正好为0。