如何在不必处理生命周期的情况下存储引用?
How to store a reference without having to deal with lifetimes?
按照the dynamic_reload
crate's example的建议,我收集了Symbol
而不是每次都提取它们,但是Symbol
需要一生。使用生命周期更改方法签名并破坏与方法 DynamicReload::update
.
的兼容性
使用 std::mem::transmute
将 Symbol
的生命周期更改为 'static
是否是有效的解决方法?
extern crate dynamic_reload;
use dynamic_reload::{DynamicReload, Lib, Symbol, Search, PlatformName, UpdateState};
use std::sync::Arc;
use std::time::Duration;
use std::thread;
use std::mem::transmute;
struct Plugins {
plugins: Vec<(Arc<Lib>, Arc<Symbol<'static, extern "C" fn() -> i32>>)>,
}
impl Plugins {
fn add_plugin(&mut self, plugin: &Arc<Lib>) {
match unsafe { plugin.lib.get(b"shared_fun[=10=]") } {
Ok(temp) => {
let f: Symbol<extern "C" fn() -> i32> = temp;
self.plugins.push((plugin.clone(), Arc::new(unsafe { transmute(f) })));
},
Err(e) => println!("Failed to load symbol: {:?}", e),
}
}
fn unload_plugins(&mut self, lib: &Arc<Lib>) {
for i in (0..self.plugins.len()).rev() {
if &self.plugins[i].0 == lib {
self.plugins.swap_remove(i);
}
}
}
fn reload_plugin(&mut self, lib: &Arc<Lib>) {
Self::add_plugin(self, lib);
}
// called when a lib needs to be reloaded.
fn reload_callback(&mut self, state: UpdateState, lib: Option<&Arc<Lib>>) {
match state {
UpdateState::Before => Self::unload_plugins(self, lib.unwrap()),
UpdateState::After => Self::reload_plugin(self, lib.unwrap()),
UpdateState::ReloadFailed(_) => println!("Failed to reload"),
}
}
}
fn main() {
let mut plugs = Plugins { plugins: Vec::new() };
// Setup the reload handler. A temporary directory will be created inside the target/debug
// where plugins will be loaded from. That is because on some OS:es loading a shared lib
// will lock the file so we can't overwrite it so this works around that issue.
let mut reload_handler = DynamicReload::new(Some(vec!["target/debug"]),
Some("target/debug"),
Search::Default);
// test_shared is generated in build.rs
match reload_handler.add_library("test_shared", PlatformName::Yes) {
Ok(lib) => plugs.add_plugin(&lib),
Err(e) => {
println!("Unable to load dynamic lib, err {:?}", e);
return;
}
}
//
// While this is running (printing a number) change return value in file src/test_shared.rs
// build the project with cargo build and notice that this code will now return the new value
//
loop {
reload_handler.update(Plugins::reload_callback, &mut plugs);
if plugs.plugins.len() > 0 {
let fun = &plugs.plugins[0].1;
println!("Value {}", fun());
}
// Wait for 0.5 sec
thread::sleep(Duration::from_millis(500));
}
}
我仍然必须将 Arc<Lib>
保留在向量中,因为 Symbol
没有实现 PartialEq
.
How to store a reference without having to deal with lifetimes?
98% 的答案是:你不会。生命周期是使用 Rust 的最大原因之一。生命周期强制执行,在编译时,您的引用将始终引用有效的内容。如果你希望 "ignore" 生命周期,那么 Rust 可能不是实现特定设计的最佳语言。您可能需要选择不同的语言或设计。
Is it a valid workaround to use std::mem::transmute
to change Symbol
's lifetime to 'static
?
transmute
是大锤子,适用于各种好的和坏的想法和实现。我鼓励永远不要直接使用它,而是将它包装在一个抽象层中,以某种方式帮助您实施适当的限制,使特定的转化正确。
如果您选择使用 transmute
,您将承担编译器之前的全部责任。确保引用始终有效将取决于您,否则您将调用未定义的行为并且您的程序被允许做任何数量的非常糟糕的事情。
对于您的具体情况,您可能 能够使用 the Rental crate to keep around "the library" and "references into the library" in a single struct that hides the lifetimes of the Symbol
s. In fact, Rental uses libloading 作为激励示例和库加载能力 dynamic_reload。看
了解更多细节和陷阱。
我不认为这会奏效,因为 DynamicReload::update
需要 &mut self
。在该方法调用期间,它很容易使所有现有引用无效。
另请参阅:
按照the dynamic_reload
crate's example的建议,我收集了Symbol
而不是每次都提取它们,但是Symbol
需要一生。使用生命周期更改方法签名并破坏与方法 DynamicReload::update
.
使用 std::mem::transmute
将 Symbol
的生命周期更改为 'static
是否是有效的解决方法?
extern crate dynamic_reload;
use dynamic_reload::{DynamicReload, Lib, Symbol, Search, PlatformName, UpdateState};
use std::sync::Arc;
use std::time::Duration;
use std::thread;
use std::mem::transmute;
struct Plugins {
plugins: Vec<(Arc<Lib>, Arc<Symbol<'static, extern "C" fn() -> i32>>)>,
}
impl Plugins {
fn add_plugin(&mut self, plugin: &Arc<Lib>) {
match unsafe { plugin.lib.get(b"shared_fun[=10=]") } {
Ok(temp) => {
let f: Symbol<extern "C" fn() -> i32> = temp;
self.plugins.push((plugin.clone(), Arc::new(unsafe { transmute(f) })));
},
Err(e) => println!("Failed to load symbol: {:?}", e),
}
}
fn unload_plugins(&mut self, lib: &Arc<Lib>) {
for i in (0..self.plugins.len()).rev() {
if &self.plugins[i].0 == lib {
self.plugins.swap_remove(i);
}
}
}
fn reload_plugin(&mut self, lib: &Arc<Lib>) {
Self::add_plugin(self, lib);
}
// called when a lib needs to be reloaded.
fn reload_callback(&mut self, state: UpdateState, lib: Option<&Arc<Lib>>) {
match state {
UpdateState::Before => Self::unload_plugins(self, lib.unwrap()),
UpdateState::After => Self::reload_plugin(self, lib.unwrap()),
UpdateState::ReloadFailed(_) => println!("Failed to reload"),
}
}
}
fn main() {
let mut plugs = Plugins { plugins: Vec::new() };
// Setup the reload handler. A temporary directory will be created inside the target/debug
// where plugins will be loaded from. That is because on some OS:es loading a shared lib
// will lock the file so we can't overwrite it so this works around that issue.
let mut reload_handler = DynamicReload::new(Some(vec!["target/debug"]),
Some("target/debug"),
Search::Default);
// test_shared is generated in build.rs
match reload_handler.add_library("test_shared", PlatformName::Yes) {
Ok(lib) => plugs.add_plugin(&lib),
Err(e) => {
println!("Unable to load dynamic lib, err {:?}", e);
return;
}
}
//
// While this is running (printing a number) change return value in file src/test_shared.rs
// build the project with cargo build and notice that this code will now return the new value
//
loop {
reload_handler.update(Plugins::reload_callback, &mut plugs);
if plugs.plugins.len() > 0 {
let fun = &plugs.plugins[0].1;
println!("Value {}", fun());
}
// Wait for 0.5 sec
thread::sleep(Duration::from_millis(500));
}
}
我仍然必须将 Arc<Lib>
保留在向量中,因为 Symbol
没有实现 PartialEq
.
How to store a reference without having to deal with lifetimes?
98% 的答案是:你不会。生命周期是使用 Rust 的最大原因之一。生命周期强制执行,在编译时,您的引用将始终引用有效的内容。如果你希望 "ignore" 生命周期,那么 Rust 可能不是实现特定设计的最佳语言。您可能需要选择不同的语言或设计。
Is it a valid workaround to use
std::mem::transmute
to changeSymbol
's lifetime to'static
?
transmute
是大锤子,适用于各种好的和坏的想法和实现。我鼓励永远不要直接使用它,而是将它包装在一个抽象层中,以某种方式帮助您实施适当的限制,使特定的转化正确。
如果您选择使用 transmute
,您将承担编译器之前的全部责任。确保引用始终有效将取决于您,否则您将调用未定义的行为并且您的程序被允许做任何数量的非常糟糕的事情。
对于您的具体情况,您可能 能够使用 the Rental crate to keep around "the library" and "references into the library" in a single struct that hides the lifetimes of the Symbol
s. In fact, Rental uses libloading 作为激励示例和库加载能力 dynamic_reload。看
我不认为这会奏效,因为 DynamicReload::update
需要 &mut self
。在该方法调用期间,它很容易使所有现有引用无效。
另请参阅: