将可变结构体传递给结构体的可变属性

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

Passing mutable struct to mutable attribute of struct

问题

我正在使用 ratatui 构建一个小型 TUI 工具,我希望将一个可变的 App 实例传递给每个小部件,以便我可以:

  • 处理事件并更新 widget 数据
  • 更新主要的 App 数据

这是我工具的简单结构:

// app.rs
impl App {
    pub fn new() -> Self {
        Self {
            user_input: UserInput::new(),
        }
    }

    pub fn get_curr_widgets(&mut self) -> Vec<&mut dyn Component> {
        vec![
            &mut self.sidebar,
            &mut self.user_input,
        ]
    }
}

// src/components/user_input.rs
impl Component for UserInput {
    fn handle_events(&mut self, event: Event, app: &mut App) {
        match event {
            Event::Key(key_event) => match key_event.code {
                // 更新 App 数据
                // KeyCode::Enter => app.do_search(),
                // app.widget_index = -1
                KeyCode::Char(to_insert) => {
                    // 更新 UserInput 数据
                    self.enter_char(to_insert);
                }
                _ => {}
            },
            _ => {}
        }
    }
}

// event_handles.rs
pub fn handle_terminal_events(event: Event, app: &mut App) {
    let widget_index = app.widget_index;
    let widgets = app.get_widgets();
    for (index, widget) in app.get_widgets().into_iter().enumerate() {
        if widget_index == index {
            // 错误:error[E0499]: 不能同时多次借用 `*app` 为可变状态
            widget.handle_events(event, app);
            break;
        }
    }
}

完整错误信息:

error[E0499]: 不能同时多次借用 `*app` 为可变状态
  --> src/handler.rs:51:43
   |
48 |         let widgets = app.get_widgets();
   |                     --------------- 第一次可变借用发生在此处
...
51 |                 widget.handle_events(event, app);
   |                      -------------        ^^^ 第二次可变借用发生在此处
   |                      |
   |                      第一次借用稍后被调用

我对于这个问题的看法在于 widget.handle_events(event, app); 这一行:

  • widget. 首次可变借用了 App
  • 随后我们无法将第二个可变引用传递给函数。

在 Rust 中,我们一次只能有一个可变引用。那么,有没有更好的方法来解决我的问题,或者我结构化错误?

英文:

I'm building a small TUI tool with ratatui, I want to pass a mutable instance of App to every widget so I can:

  • Handle events and update widget data
  • Update main App data

Here is a simple structure of my tool

// app.rs
impl App {
	pub fn new() -&gt; Self {
		Self {
			user_input: UserInput::new(),
		}
	}

	pub fn get_curr_widgets(&amp;mut self) -&gt; Vec&lt;&amp;mut dyn Component&gt; {
		vec![
			&amp;mut self.sidebar,
			&amp;mut self.user_input,
		]
	}
}

// src/components/user_input.rs
impl Component for UserInput {
	fn handle_events(&amp;mut self, event: Event, app: &amp;mut App) {
		match event {
			Event::Key(key_event) =&gt; match key_event.code {
				// Update App data
				// KeyCode::Enter =&gt; app.do_search(),
				// app.widget_index = -1
				KeyCode::Char(to_insert) =&gt; {
					// Update UserInput data
					self.enter_char(to_insert);
				}
				_ =&gt; {}
			},
			_ =&gt; {}
		}
	}
}

// event_handles.rs
pub fn handle_terminal_events(event: Event, app: &amp;mut App) {
	let widget_index = app.widget_index;
	let widgets = app.get_widgets();
	for (index, widget) in app.get_widgets().into_iter().enumerate() {
		if widget_index == index {
			// Error: error[E0499]: cannot borrow `*app` as mutable more than once at a time
			widget.handle_events(event, app);
			break;
		}
	}
}

Full error:

error[E0499]: cannot borrow `*app` as mutable more than once at a time
  --&gt; src/handler.rs:51:43
   |
48 |         let widgets = app.get_widgets();
   |                     --------------- first mutable borrow occurs here
...
51 |                 widget.handle_events(event, app);
   |                      -------------        ^^^ second mutable borrow occurs here
   |                      |
   |                      first borrow later used by call

What I think of the error at line: widget.handle_events(event, app);

  • widget. first borrow mutable of App
  • then we can't pass the second mutable of App to the function

In Rust, we only can have one mutable ref at a time.
So what are better ways to solve my problem or I'm structuring it wrong?

答案1

得分: 1

这似乎是一个设计问题。
Widgets(组件)被包含在App中,但您试图在组件级别处理事件时改变App。

理想情况下,改变App的代码应该存在于“impl App”块中。

impl App {
    pub fn new() -> Self {
        Self {
            user_input: UserInput::new(),
        }
    }

    pub fn handle_events(&mut self) {
        let mut widgets = vec![
            &mut self.sidebar,
            &mut self.user_input,
        ];
        for (index, widget) in self.get_widgets().iter_mut().enumerate() {
            if widget_index == index {
                widget.handle_events(event, self);
                break;
            }
        }
    }
}

// src/components/user_input.rs
impl Component for UserInput {
    fn handle_events(&mut self, event: Event, app: &mut App) {
        match event {
            Event::Key(key_event) => match key_event.code {
                // 更新App数据
                // KeyCode::Enter => app.do_search(),
                // app.widget_index = -1
                KeyCode::Char(to_insert) => {
                    // 更新UserInput数据
                    self.enter_char(to_insert);
                }
                _ => {}
            },
            _ => {}
        }
    }
}

// event_handles.rs
pub fn handle_terminal_events(event: Event, app: &mut App) {
    app.handle_events();
}

因此,通过此更改,您仅在App impl的上下文(生命周期)中改变了App。

这应该解决了问题。

注意:通常使用get方法来读取(&)而不是写入(&mut)。

英文:

This smells like a design problem.
Widgets (Components) are enclosed within App but you are trying to mutate the app when handling events at the Component level.

Ideally the code that mutates the App should exist within the "impl App" block.

impl App {
    pub fn new() -&gt; Self {
        Self {
            user_input: UserInput::new(),
        }
    }

    pub fn handle_events(&amp;mut self) {
        let mut widgets = vec![
            &amp;mut self.sidebar,
            &amp;mut self.user_input,
        ];
        for (index, widget) in self.get_widgets().iter_mut().enumerate() {
            if widget_index == index {
                widget.handle_events(event, self);
                break;
            }
        }
    }
}

// src/components/user_input.rs
impl Component for UserInput {
    fn handle_events(&amp;mut self, event: Event, app: &amp;mut App) {
        match event {
            Event::Key(key_event) =&gt; match key_event.code {
                // Update App data
                // KeyCode::Enter =&gt; app.do_search(),
                // app.widget_index = -1
                KeyCode::Char(to_insert) =&gt; {
                    // Update UserInput data
                    self.enter_char(to_insert);
                }
                _ =&gt; {}
            },
            _ =&gt; {}
        }
    }
}

// event_handles.rs
pub fn handle_terminal_events(event: Event, app: &amp;mut App) {
    app.handle_events();
}

So by this change you are mutating the app within the context(lifetime) of App impl only.

This should resolve the issue.

NOTE: get methods are conventionally used to read(&) not write (&mut).

huangapple
  • 本文由 发表于 2023年7月24日 17:39:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/76753181.html
匿名

发表评论

匿名网友

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

确定