如何在不处理生命周期的情况下存储参考? [英] How to store a reference without having to deal with lifetimes?

查看:91
本文介绍了如何在不处理生命周期的情况下存储参考?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据 dynamic_reload板条箱示例的建议,我收集了Symbol s而不是提取每次都使用它们,但是Symbol需要使用一生.使用生命周期会更改方法签名,并破坏与方法DynamicReload::update的兼容性.

使用std::mem::transmuteSymbol的生存期更改为'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\0") } {
            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.

解决方案

如何在不处理生命周期的情况下存储参考?

在98%的情况下,答案是:您不.使用寿命是使用Rust的最大原因之一.生命周期强制在编译时 ,使您的引用始终引用有效的内容.如果您希望忽略"生命周期,那么Rust可能不是实现特定设计的最佳语言.您可能需要选择其他语言或设计.

使用std::mem::transmuteSymbol的生存期更改为'static是有效的解决方法吗?

transmute是The Big Hammer,适用于各种好主意和实现.我鼓励不要直接使用它,而应将其包装在抽象层中,以某种方式帮助您实施适当的限制,以使特定的转换正确无误.

如果选择使用transmute,则假定编译器以前具有全部责任.确保引用始终有效,这取决于您,否则,您将调用未定义的行为,并且您的程序可以执行任何数量的非常糟糕"的事情.


对于您的具体情况,您可能可以使用租赁箱在隐藏Symbol生命周期的单个结构中保留库"和对库的引用".实际上,Rental使用 libloading 作为激励示例,并且libloading具有dynamic_reload的功能.看 为什么我不能在同一结构中存储值和对该值的引用?以获取更多详细信息和陷阱. /p>

我并不乐观,因为 DynamicReload::update 需要&mut self.在该方法调用期间,它很容易使所有现有引用无效.

另请参阅:

As suggested by the dynamic_reload crate's example, I collected Symbols instead of extracting them every time, but Symbol requires a lifetime. Using a lifetime changes method signatures and breaks the compatibility with method DynamicReload::update.

Is it a valid workaround to use std::mem::transmute to change Symbol's lifetime to '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\0") } {
            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));
    }
}

I still have to keep Arc<Lib> inside the vector because Symbol doesn't implement PartialEq.

解决方案

How to store a reference without having to deal with lifetimes?

The answer in 98% of the cases is: you don't. Lifetimes are one of the biggest reasons to use Rust. Lifetimes enforce, at compile time, that your references will always refer to something that is valid. If you wish to "ignore" lifetimes, then perhaps Rust may not the best language to realize a particular design. You may need to pick a different language or design.

Is it a valid workaround to use std::mem::transmute to change Symbol's lifetime to 'static?

transmute is The Big Hammer, suitable for all sorts of good and bad ideas and implementations. I would encourage never using it directly, but instead wrapping it in a layer of abstraction that somehow helps you enforce the appropriate restrictions that make that particular transmute correct.

If you choose to use transmute, you are assuming the full responsibility that the compiler previously had. It will be up to you to ensure that the reference is always valid, otherwise you are invoking undefined behavior and your program is allowed to do any number of Very Bad things.


For your specific case, you may be able to use the Rental crate to keep around "the library" and "references into the library" in a single struct that hides the lifetimes of the Symbols. In fact, Rental uses libloading as the motivating example and libloading powers dynamic_reload. See Why can't I store a value and a reference to that value in the same struct? for more details and pitfalls.

I'm not optimistic that this will work because DynamicReload::update requires a &mut self. During that method call, it could easily invalidate all of the existing references.

See also:

这篇关于如何在不处理生命周期的情况下存储参考?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆