化妆品产品的自建网站哟哪些,wordpress电子商务主题下载,网站开发课程总结,在线音乐网站开发数据库Rust的所有权问题#xff0c;在我学Rust的时候就跳过了#xff0c;因为我知道这玩意儿没有场景就不好理解。没想到场景很快就出现了。 在开发Yew应用组件的时候#xff0c;涉及到了事件#xff0c;闭包#xff0c;自然就引出了所有权问题。 话不多说#xff0c;下面让我们…Rust的所有权问题在我学Rust的时候就跳过了因为我知道这玩意儿没有场景就不好理解。没想到场景很快就出现了。 在开发Yew应用组件的时候涉及到了事件闭包自然就引出了所有权问题。 话不多说下面让我们直接进入代码场景去体验并了解Rust的所有权机制吧。
下面这段代码是能够正常工作的。这段代码的逻辑意图也很简单这是一个函数式的编辑组件在这个组件中有一个保存按钮。当用户点击保存按钮时触发handle_save事件在handle_save事件中获取input和textarea的值并将其通过在Props上定义的on_save Callback传递给外部组件。
#[function_component(Editor1)]
pub fn editor1(props: Props) - Html {let title_ref use_node_ref();let content_ref use_node_ref();let Props { on_save, .. } props;let handle_save {let title_ref title_ref.clone();let content_ref content_ref.clone();let on_save on_save.clone();Callback::from(move |_| {let title: String if let Some(title) title_ref.cast::HtmlInputElement() {title.value()} else {none.to_string()};let content: String if let Some(content) content_ref.cast::HtmlTextAreaElement() {content.value()} else {none.to_string()};on_save.emit(EditorData { title, content })})};html! {div classeditordiv classtitleinput typetext ref{title_ref}/Button text保存 onclick{handle_save}//divdiv classcontenttextarea value{props.data.content.clone()} ref{content_ref}//div/div}
}Rust的所有权机制最开始虽然不太好理解但是它的编译器能够在编译时把代码的问题找出来。所以我们也没有必要抱怨。在Rust中只要是编译通过的代码都是好代码哈哈哈。 我们都知道在不讲所有权机制的编程语言中变量的使用都比较随意它们都会被GC或者其它内存管理机制照料。因此我的第一版代码写出来是这个样子。
#[function_component(Editor1)]
pub fn editor1(props: Props) - Html {let title_ref use_node_ref();let content_ref use_node_ref();let handle_save Callback::from(move |_| {let title: String if let Some(title) title_ref.cast::HtmlInputElement() {title.value()} else {none.to_string()};});...
}在这段代码中直接在闭包中使用了title_ref变量虽然已经很有Rust的语言特色了但是还是错了编译器给出错误提示如下
error[E0382]: use of moved value: title_ref-- src/components/editor1.rs:45:41|
31 | let title_ref use_node_ref();| --------- move occurs because title_ref has type yew::NodeRef, which does not implement the Copy trait
...
34 | let handle_save Callback::from(move |_| {| -------- value moved into closure here
35 | let title: String if let Some(title) title_ref.cast::HtmlInputElement() {| --------- variable moved due to use in closure
...
45 | input typetext ref{title_ref}/| ^^^^^^^^^ value used here after moveFor more information about this error, try rustc --explain E0382.Rust的编译器给我们做了详细的解释特别的运行rustc --explain E0382还能看到关于所有权的更加详细的解释。 让我来逐一解读编译器提示及报错以便让我们更好的了解Rust的所有权机制。
第一个提示
move occurs because title_ref has type yew::NodeRef, which does not implement the Copy traittitle_ref是一个类型为yew::NodeRef且没有实现Copy trait的变量。这里面“没有实现Copy trait”很关键。在我们之前的经历中遇到Copy trait相关的问题只需要调用它的.clone()方法就可以解决。如果它没有clone方法就在它的类型定义上加上#[derive(Clone)]。
第二个提示
value moved into closure here因为有move关键字因此在闭包中所有的变量的所有权都会被移动包括title_ref。问题来了如果我们把move关键字移除掉又会是什么提示呢 让我们开一个小差把代码修改成下面这个样子。 let handle_save Callback::from(|_| {let title: String if let Some(title) title_ref.cast::HtmlInputElement() {title.value()} else {none.to_string()};});编译器的提示和报错如下
error[E0373]: closure may outlive the current function, but it borrows title_ref, which is owned by the current function-- src/components/editor1.rs:34:38|
34 | let handle_save Callback::from(|_| {| ^^^ may outlive borrowed value title_ref
35 | let title: String if let Some(title) title_ref.cast::HtmlInputElement() {| --------- title_ref is borrowed here|
note: function requires argument type to outlive static-- src/components/editor1.rs:34:23|
34 | let handle_save Callback::from(|_| {| _______________________^
35 | | let title: String if let Some(title) title_ref.cast::HtmlInputElement() {
36 | | title.value()
37 | | } else {
38 | | none.to_string()
39 | | };
40 | | });| |______^
help: to force the closure to take ownership of title_ref (and any other referenced variables), use the move keyword|
34 | let handle_save Callback::from(move |_| {|
这个提示告诉我们闭包的生命周期可能超出了当前函数的生命周期但是闭包中借用了title_ref变量。也就是所可能当前函数销毁了但是闭包还存在。这个时候在闭包中借用title_ref肯定是不合乎逻辑的因此编译器立即拒绝了这段代码最后给出了添加move关键字的建议。所以我们又回到之前的那个讨论。
第三个提示
variable moved due to use in closure这个提示告诉我们变量title_ref的所有权被移动是因为这一行代码上使用了该变量。不得不说这个提示很精确。
第四个报错
value used here after move这是一个报错信息在终端上显示的是红色但是在这里没有把红色显示出来。这个信息告诉我们title_ref的所有权已经被移动了不能再使用title_ref了。 这个报错对于从其它开发语言转过来的同学可能有点匪夷所思。就好像我名下的财产弄来弄去最后变成不是我了我找谁说理去哇。 但这就是Rust的“财产”管理规则。还好它的编译器比较讲道理把错误第四以及这个错误是怎么形成的第一到第三都给你说清楚了。 因此要在闭包中使用title_ref我们得clone一下。 需要注意的是如果这个对象已经实现了Copy trait编译器会自动生成调用copy的代码就没有必要显示的调用clone()了。但我们的大部分struct只能通过#[derive(Clone)]来实现Clone trait。而通过#[derive(Copy)]来获取Copy trait大部分情况由于里面的字段例如String类型不支持Copy trait以失败告终。 因此我们把代码修改成下面这样就不会报错了。
#[function_component(Editor1)]
pub fn editor1(props: Props) - Html {let title_ref use_node_ref();let content_ref use_node_ref();let title_ref1 title_ref.clone();let handle_save Callback::from(move |_| {let title: String if let Some(title) title_ref1.cast::HtmlInputElement() {title.value()} else {none.to_string()};});...}
}然后我参考了一下Yew官方的一些例子代码他们通过block的方式优雅的处理了这个所有权转移问题在我看来至少变量名称干扰少了但是这种写法在其它语言中不推荐哇因为这个是一个赤裸裸的name shadow哇 let handle_save {let title_ref title_ref.clone();Callback::from(move |_| {let title: String if let Some(title) title_ref.cast::HtmlInputElement() {title.value()} else {none.to_string()};})};好了关于Rust语言的所有权机制我暂时就讲到这里这只是它的冰山一角希望给大家讲清楚了。听说还有更高级的用法关于RcRefCell或ArcMutex如果后面有遇到再来和大家分享。