share a struct with lifetime parameter which implements `dyn` trait across threads

huangapple go评论107阅读模式
英文:

share a struct with lifetime parameter which implements `dyn` trait across threads

问题

如何安全地在线程之间共享实现了具有生命周期参数的dyn trait的结构体。这是我的代码:

main.rs

use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, mpsc, Mutex};
use std::thread;

trait Draw {
    fn draw(&self);
}

#[derive(Default)]
struct Button {
    
}

impl Draw for Button {
    fn draw(&self) {
        println!("draw button");
    }
}

#[derive(Default)]
struct SelectionBox {
    
}

impl Draw for SelectionBox {
    fn draw(&self) {
        println!("draw selection box");
    }
}

#[derive(Default)]
struct TextField {
    
}

impl Draw for TextField {
    fn draw(&self) {
        println!("draw text field");
    }
}


pub struct RunningThreadInterface<'a, T> {
    pub instance: Arc<T>,
    pub thread_join_handle: thread::JoinHandle<()>,
}

pub trait StartThread<'a, T> {
    fn start(self, thread_id: String) -> RunningThreadInterface<'a, T>;
    fn run(&self);
}

pub trait TerminateThread {
    fn stop(&mut self);
    fn wait(self);
}

struct Screen<'a> {
    widgets: Mutex<Vec<&'a (dyn Draw + Send + Sync)>>,
    rx: Mutex<mpsc::Receiver<String>>,
    terminate_flag: AtomicBool,
}

impl<'a> Screen<'a> {
    fn new(rx: mpsc::Receiver<String>) -> Screen<'a> {
        Screen {
            widgets: Mutex::new(Vec::new()),
            rx: Mutex::new(rx),
            terminate_flag: AtomicBool::new(false),
        }
    }
    
    fn add(&mut self, widget: &'a (dyn Draw + Send + Sync)) {
        self.widgets.lock().unwrap().push(widget);
    }
    
    fn draw_widgets(&self) {
        for widget in &*self.widgets.lock().unwrap() {
            widget.draw();
        }
    }
}

impl<'a> StartThread<'a, Screen<'a>> for Screen<'a> {
    fn start(self, thread_id: String) -> RunningThreadInterface<'a, Screen<'a>> {
        let screen = Arc::new(self);
        RunningThreadInterface {
            instance: Arc::clone(&screen),
            thread_join_handle: thread::Builder::new().name(thread_id).spawn(move || screen.run()).ok().unwrap(),
        }
    }

    fn run(&self) {
        while !self.terminate_flag.load(Ordering::SeqCst) {
            self.rx.lock().unwrap().recv().unwrap();
        }
    }
}

impl<'a> TerminateThread for RunningThreadInterface<'a, Screen<'a>> {
    fn stop(&mut self) {
        self.instance.terminate_flag.store(true, Ordering::SeqCst);
    }

    fn wait(self) {
        self.thread_join_handle.join();
    }
}

fn main() {
    let button: Button = Default::default();
    let selection_box: SelectionBox = Default::default();
    let text_field: TextField = Default::default();
    
    let (_tx, rx) = mpsc::channel();
    
    let mut screen = Screen::new(rx);
    screen.add(&button);
    screen.add(&selection_box);
    screen.add(&text_field);
    
    screen.draw_widgets();
    
    println!("");
    
    button.draw();
    selection_box.draw();
    text_field.draw();
}

错误:

error[E0521]: borrowed data escapes outside of method
--> src/main.rs:90:33
|
85 | impl<'a> StartThread<'a, Screen<'a>> for Screen<'a> {
|      -- lifetime `'a` defined here
86 |     fn start(self, thread_id: String) -> RunningThreadInterface<'a, Screen<'a>> {
|              ---- `self` is a reference that is only valid in the method body
...
90 |             thread_join_handle: thread::Builder::new().name(thread_id).spawn(move || screen.run()).ok().unwrap(),
|                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|                                 |
|                                 `self` escapes the method body here
|                                 argument requires that `'a` must outlive `'static`
|
= note: requirement occurs because of the type `Screen<'_>`
= note: the struct `Screen<'a>` is invariant over the parameter `'a`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

请注意,我不能更改现有的StartThreadTerminateThread trait的语法,但如果现有设计不好(可以改进),我愿意听取建议。

英文:

How to share a struct with lifetime parameter which implements dyn trait across threads safely. This is my code

main.rs

use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, mpsc, Mutex};
use std::thread;
trait Draw {
fn draw(&amp;self);
}
#[derive(Default)]
struct Button {
}
impl Draw for Button {
fn draw(&amp;self) {
println!(&quot;draw button&quot;);
}
}
#[derive(Default)]
struct SelectionBox {
}
impl Draw for SelectionBox {
fn draw(&amp;self) {
println!(&quot;draw selection box&quot;);
}
}
#[derive(Default)]
struct TextField {
}
impl Draw for TextField {
fn draw(&amp;self) {
println!(&quot;draw text field&quot;);
}
}
pub struct RunningThreadInterface&lt;T&gt; {
pub instance: Arc&lt;T&gt;,
pub thread_join_handle: thread::JoinHandle&lt;()&gt;,
}
pub trait StartThread&lt;T&gt; {
fn start(self, thread_id: String) -&gt; RunningThreadInterface&lt;T&gt;;
fn run(&amp;self);
}
pub trait TerminateThread {
fn stop(&amp;mut self);
fn wait(self);
}
struct Screen&lt;&#39;a&gt; {
widgets: Mutex&lt;Vec&lt;&amp;&#39;a (dyn Draw + Send + Sync)&gt;&gt;,
rx: Mutex&lt;mpsc::Receiver&lt;String&gt;&gt;,
terminate_flag: AtomicBool,
}
impl&lt;&#39;a&gt; Screen&lt;&#39;a&gt; {
fn new(rx: mpsc::Receiver&lt;String&gt;) -&gt; Screen&lt;&#39;a&gt; {
Screen {
widgets: Mutex::new(Vec::new()),
rx: Mutex::new(rx),
terminate_flag: AtomicBool::new(false),
}
}
fn add(&amp;mut self, widget: &amp;&#39;a (dyn Draw + Send + Sync)) {
self.widgets.lock().unwrap().push(widget);
}
fn draw_widgets(&amp;self) {
for widget in &amp;*self.widgets.lock().unwrap() {
widget.draw();
}
}
}
impl&lt;&#39;a&gt; StartThread&lt;Screen&lt;&#39;a&gt;&gt; for Screen&lt;&#39;a&gt; {
fn start(self, thread_id: String) -&gt; RunningThreadInterface&lt;Screen&lt;&#39;a&gt;&gt; {
let screen = Arc::new(self);
RunningThreadInterface {
instance: Arc::clone(&amp;screen),
thread_join_handle: thread::Builder::new().name(thread_id).spawn(move || screen.run()).ok().unwrap(),
}
}
fn run(&amp;self) {
while !self.terminate_flag.load(Ordering::SeqCst) {
self.rx.lock().unwrap().recv().unwrap();
}
}
}
impl&lt;&#39;a&gt; TerminateThread for RunningThreadInterface&lt;Screen&lt;&#39;a&gt;&gt; {
fn stop(&amp;mut self) {
self.instance.terminate_flag.store(true, Ordering::SeqCst);
}
fn wait(self) {
self.thread_join_handle.join();
}
}
fn main() {
let button: Button = Default::default();
let selection_box: SelectionBox = Default::default();
let text_field: TextField = Default::default();
let (_tx, rx) = mpsc::channel();
let mut screen = Screen::new(rx);
screen.add(&amp;button);
screen.add(&amp;selection_box);
screen.add(&amp;text_field);
screen.draw_widgets();
println!(&quot;&quot;);
button.draw();
selection_box.draw();
text_field.draw();
}

Error

error[E0521]: borrowed data escapes outside of method
--&gt; src/main.rs:90:33
|
85 | impl&lt;&#39;a&gt; StartThread&lt;Screen&lt;&#39;a&gt;&gt; for Screen&lt;&#39;a&gt; {
|      -- lifetime `&#39;a` defined here
86 |     fn start(self, thread_id: String) -&gt; RunningThreadInterface&lt;Screen&lt;&#39;a&gt;&gt; {
|              ---- `self` is a reference that is only valid in the method body
...
90 |             thread_join_handle: thread::Builder::new().name(thread_id).spawn(move || screen.run()).ok().unwrap(),
|                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|                                 |
|                                 `self` escapes the method body here
|                                 argument requires that `&#39;a` must outlive `&#39;static`
|
= note: requirement occurs because of the type `Screen&lt;&#39;_&gt;`, which makes the generic argument `&#39;_` invariant
= note: the struct `Screen&lt;&#39;a&gt;` is invariant over the parameter `&#39;a`
= help: see &lt;https://doc.rust-lang.org/nomicon/subtyping.html&gt; for more information about variance
For more information about this error, try `rustc --explain E0521`.

Please note I cannot change the existing syntax of StartThread and TerminateThread traits, but I'm open for suggestions if the exiting design isn't good(can be made better)

答案1

得分: 1

I don't know if this generic enough for you, but if you restrict the generic lifetime to be static (impl<'a: 'static> StartThread<Screen<'a>> for Screen<'a>) then your example runs.

You can also do it in this equivalent way:

impl StartThread<Screen<'static>> for Screen<'static> {
    fn start(self, thread_id: String) -> RunningThreadInterface<Screen<'static>> {
        let screen = Arc::new(self);
        RunningThreadInterface {
            instance: Arc::clone(&screen),
            thread_join_handle: thread::Builder::new().name(thread_id).spawn(move || screen.run()).ok().unwrap(),
        }
    }

    fn run(&self) {
        while !self.terminate_flag.load(Ordering::SeqCst) {
            self.rx.lock().unwrap().recv().unwrap();
        }
    }
}
英文:

I don't know if this generic enough for you, but if you restrict the generic lifetime to be static (impl&lt;&#39;a: &#39;static&gt; StartThread&lt;Screen&lt;&#39;a&gt;&gt; for Screen&lt;&#39;a&gt;) then your example runs.

You can also do it in this equivalent way:

impl StartThread&lt;Screen&lt;&#39;static&gt;&gt; for Screen&lt;&#39;static&gt; {
    fn start(self, thread_id: String) -&gt; RunningThreadInterface&lt;Screen&lt;&#39;static&gt;&gt; {
        let screen = Arc::new(self);
        RunningThreadInterface {
            instance: Arc::clone(&amp;screen),
            thread_join_handle: thread::Builder::new().name(thread_id).spawn(move || screen.run()).ok().unwrap(),
        }
    }

    fn run(&amp;self) {
        while !self.terminate_flag.load(Ordering::SeqCst) {
            self.rx.lock().unwrap().recv().unwrap();
        }
    }
}

huangapple
  • 本文由 发表于 2023年5月26日 15:08:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76338395.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定