英文:
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
请注意,我不能更改现有的StartThread
和TerminateThread
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(&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<T> {
pub instance: Arc<T>,
pub thread_join_handle: thread::JoinHandle<()>,
}
pub trait StartThread<T> {
fn start(self, thread_id: String) -> RunningThreadInterface<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<Screen<'a>> for Screen<'a> {
fn start(self, thread_id: String) -> RunningThreadInterface<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<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
error[E0521]: borrowed data escapes outside of method
--> src/main.rs:90:33
|
85 | impl<'a> StartThread<Screen<'a>> for Screen<'a> {
| -- lifetime `'a` defined here
86 | fn start(self, thread_id: String) -> RunningThreadInterface<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<'_>`, which makes the generic argument `'_` invariant
= 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
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<'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();
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论