[2023.09.13]: Rust Lang,避不开的所有权问题
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 class="editor"><div class="title"><input type="text" ref={title_ref}/><Button text="保存" onclick={handle_save}/></div><div class="content"><textarea 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 type="text" 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` trait
title_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语言的所有权机制,我暂时就讲到这里,这只是它的冰山一角,希望给大家讲清楚了。听说还有更高级的用法,关于Rc<RefCell<>>或Arc<Mutex<>>,如果后面有遇到,再来和大家分享。
相关文章:
[2023.09.13]: Rust Lang,避不开的所有权问题
Rust的所有权问题,在我学Rust的时候就跳过了,因为我知道这玩意儿没有场景就不好理解。没想到场景很快就出现了。 在开发Yew应用组件的时候,涉及到了事件,闭包,自然就引出了所有权问题。 话不多说,下面让我们…...
Redux中间件源码解析与实现
基本介绍 本文中涉及到的关键npm包的版本信息如下: react 的版本为18.2.0 redux的版本为4.1.2 redux-thunk版本为2.4.2 redux-promise版本为0.6.0 redux-logger版本为3.0.6 在Redux源码解析与实现(一)Redux源码解析与实现(二&…...
关于rsync用不了之后
1.尝试找出rsync使用错误原因: 我遇见一个问题:rsync:read errors mapping:communication error on send (70),我查了一下这个问题很大可能是网络链接导致的,然后我用nslookup指令查看了/train2…...
由一个多线程并发保存而引发的思考
1. 问题描述 问题描述,现A表(用户查看实验的次数)有user_id和exp_id两个字段,其中user_id加exp_id不唯一。B表有user_id,exp_id以及exp_num三个字段,其中user_id加exp_id唯一(表中未建唯一索引)。 现需要将A表的数据同步到B表,单机模式通过定时任务同步。A表的数据有…...
python-vlc
文章目录 关于 python-vlc安装使用关于 python-vlc Python vlc bindings github : https://github.com/oaubert/python-vlcPython bindings (ctypes-based) for VLC http://olivieraubert.net/vlc/python-ctypes/关于 VLC 可参考 macOS - 安装使用 VLC https://blog.csdn.net/…...
2023长城杯 web部分题目(seekingeasy_extension)
seeking 下载题目附件得到: <?php error_reporting(0); header("HINT:POST n range(1,10)");$image $_GET[image]; echo "这里什么也没有,或许吧。"; $allow range(1, 10); shuffle($allow); if (($_POST[n] $allow[0])) …...
2-1 张量数据结构
张量概念 张量是什么? 单个元素叫标量(scalar),一个序列叫向量(vector),多个序列组成的平面叫矩阵(matrix),多个平面组成的立方体叫张量(tensor&…...
QSqlQuery查询语句
SqlQuery 封装了在 QSqlDatabase 上执行的 SQL 查询中创建、导航和检索数据所涉及的功能。 可用于执行 DML(数据操作语言)语句,如 SELECT、INSERT、UPDATE 和 DELETE, 以及 DDL(数据定义语言)语句ÿ…...
用c语言编写出三底模型
以下是一个用C语言实现三底模型的示例代码。这个程序通过循环遍历输入的股票数据,判断是否出现三底形态,如果是,则输出买入信号,否则输出卖出信号。 c语言 #include <stdio.h> #include <stdlib.h> // 判断是否出现…...
15 Python使用MySQL
概述 在上一节,我们介绍了如何在Python中使用网络,包括:套接字编程、socketserver等内容。在这一节,我们将介绍如何在Python中使用MySQL。MySQL是最流行的关系型数据库管理系统之一,由瑞典MySQL AB公司开发,…...
3、Nginx 常用的命令和配置文件
文章目录 3、nginx 常用的命令和配置文件3.1 nginx 常用的命令:3.2 nginx.conf 配置文件3.2.1 地址3.2.2 内容3.2.2 vim不正常退出后再次打开信息提示解决方法 3.3 第一部分:全局块3.4 第二部分:events 块3.4 第三部分:http 块①、…...
python经典百题之兔子出生问题
这是一个经典的 Fibonacci 数列问题,可以通过递归或循环来解决。 递归方法: 由题意可知,第 n 个月的兔子数等于第 n-1 个月的兔子数加上第 n-2 个月的兔子数。设 f(n) 表示第 n 个月的兔子数,则有: f(n) f(n-1) f…...
不定积分的概念和性质
目录 原函数 不定积分 不定积分的几何意义 原函数的存在定理 不定积分的性质 不定积分是微积分的一个关键部分,它涉及到一个函数的不定积分的计算。不定积分可以理解为求一个函数的原函数,也被称为反导数。原函数是一个函数,使得该函数的…...
远程访问服务器JupyterLab的配置方法
远程访问服务器JupyterLab的配置方法 环境及工具注意 基本步骤生成密码生成并修改配置文件*错误:jupyter localhost 已拒绝连接*后台运行jupyter后台关闭 其实就是在服务器运行JupyterLab,然后在本地浏览器访问 环境及工具 服务器:Ubuntu 1…...
Java native 关键字
如你在看 JDK 的源代码的时候,大概率会看到很多方法使用了 native 关键字。 下面是 String 对象 JDK 中的源代码,就带有了一个 native 关键字。 native 是干什么用的 简单来说就是 Java 的 native 方法的实现不是用 Java 实现的,可能在其他…...
【线性代数】沉浸式线性代数在线学习网站
地址:http://immersivemath.com/ila/index.html 这是全球第一本带交互式图形的线性代数教材,作者是 J. Strm, K. strm, and T. Akenine-Mller。 全书一共十章,各章节内容如下: 接下来我将对各章节进行简单的总结,另外…...
Kotlin中特性、数据类、伴生对象、顶层函数
Kotlin中的函数参数和属性声明 在 Kotlin 中,函数参数和属性有不同的声明方式和行为。这些特性使得 Kotlin 代码更加安全、易于理解和维护。 函数参数的只读性 fun sum(a: Int, b: Int): Int {var modifiedA aif (modifiedA > 0) {modifiedA 1}//三元表达式v…...
《PostgreSQL物化视图:创建、维护与应用》
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🐅🐾猫头虎建议程序员必备技术栈一览表📖: 🛠️ 全栈技术 Full Stack: 📚…...
shell脚本之test命令
shell脚本之test命令 数值比较:2. 字符串比较:3. 文件测试:4. 逻辑操作:5. 其他测试: test命令在Shell脚本中用于进行条件测试和条件判断。它用于检查文件、字符串和数值的各种条件,并返回一个状态码&#…...
JAVA设计模式8:装饰模式,动态地将责任附加到对象上,扩展对象的功能
作者主页:Designer 小郑 作者简介:3年JAVA全栈开发经验,专注JAVA技术、系统定制、远程指导,致力于企业数字化转型,CSDN博客专家,阿里云社区专家博主,蓝桥云课讲师。 目录 一、什么是装饰模式二、…...
Linux学习之MySQL备份
xtrabackup资源下载 完全备份与恢复 # 1.物理备份与恢复 # 冷备份,需停止数据库服务 适合线下服务器。 [rootmysql50 ~]# systemctl stop mysqld [rootmysql50 ~]# mkdir /bakdir [rootmysql50 ~]# cp -r /var/lib/mysql /bakdir/mysql.bak [rootmysql50 ~]# cd /…...
时序分解 | MATLAB实现北方苍鹰优化算法NGO优化VMD信号分量可视化
时序分解 | MATLAB实现北方苍鹰优化算法NGO优化VMD信号分量可视化 目录 时序分解 | MATLAB实现北方苍鹰优化算法NGO优化VMD信号分量可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 北方苍鹰优化算法NGO优化VMD,对其分解层数,惩罚因子数做优化…...
分类预测 | Matlab实现RBF-Adaboost多特征分类预测
分类预测 | Matlab实现RBF-Adaboost多特征分类预测 目录 分类预测 | Matlab实现RBF-Adaboost多特征分类预测效果一览基本介绍研究内容程序设计参考资料 效果一览 基本介绍 1.Matlab实现基于RBF-Adaboost数据分类预测(Matlab完整程序和数据) 2.多特征输入…...
【Java代码规范】阿里编码规约 VS CheckStyle
全文速览: 1、关于代码编码质量2、如何小成本有效管理企业内的编码规范 2.1 阿里编码规约IDE插件2.2 CheckStyle IDE插件 3、如何在代码提交中检验规范 3.1 阿里编码规约配置git precommit check3.2 CheckStyle配置git precommit check3.3 实践 1、关于代码编码质…...
iPhone苹果15手机圆点怎么设置让屏幕上显示出来圆形图标?
iPhone苹果15手机圆点怎么设置让屏幕上显示出来圆形图标? 1、在iPhone苹果手机上找到「设置」并点击打开; 2、在苹果iPhone设置内找到「辅助功能」并点击打开; 3、在苹果iPhone手机辅助功能内的动作交互内找到「触控」并点击打开;…...
kibana报错内存溢出问题解决
一、背景: kibana内存溢出,进程被kill掉,导致前端页面访问不到。 报错内容 二、报错原因: 发现是前端 js 报的内存 oom 异常,通过网上资料发现node.js 的默认内存大小为1.4G Node 中通过 JavaScript 使用内存时只能…...
【C语法】1124循环结构
#include <stdio.h> int main(){ //输入一个数。倒叙输出各个位上的数。 //123456 6 5 4 3 2 1 // 1输出个位上的数 %10 // 2将这个数去掉个位上的数。 / 10 // 3当这个数最后变成0时结束 int a 0; printf("请输入一个正整数:"); scanf…...
在PHP8中向数组添加元素-PHP8知识详解
在php8中向数组添加元素有多种方法,在这里主要讲解几个常用的方法:使用方括号[]添加元素、使用array_unshift()函数,向数组的头部添加元素、使用array_push()函数,向数组的尾部添加元素、使用array_splice()函数添加元素。 1、使用…...
Drupal __ 8.5.0 __ XSS文件上传 __CVE-2019-6341
Drupal __ 8.5.0 __ XSS文件上传 __CVE-2019-6341 说明内容漏洞编号CVE-2019-6341漏洞名称Drupal XSS漏洞漏洞评级中危影响范围在7.65之前的Drupal 7版本中; 8.6.13之前的Drupal 8.6版本; 8.5.14之前的Drupal 8.5版本。漏洞描述Drupal诞生于2000年,是一…...
Pycharm中配置Celery启动
Pycharm中配置Celery启动 前置条件 目录结构 ----FerDemo --------celery_demo ------------tasks.py tasks.py文件代码 import sys import time from celery import Celeryapp Celery(demo,backendredis://:password127.0.0.1/0,brokerredis://:password127.0.0.1/1,broker…...
景安网络网站建设/免费网站推广网站破解版
2019独角兽企业重金招聘Python工程师标准>>> 一、 基本介绍 Celery是一个专注于实时处理和任务调度的分布式任务队列。所谓任务就是消息,消息中的有效载荷中包含要执行任务需要的全部数据。 使用Celery常见场景: Web应用。当用户触发的一个操…...
中学网站域名用什么用/百度竞价排名是什么方式
目录大纲序认识Qt学习网站安装Qt在Linux时在Windows时32位系统与x86系统关系构建项目及设置配置Qt右击上下文菜单的某些选项之间区别创建主窗口、设置窗口居中、dialog与widget与mainWindow比较设置QLabel文本居中QLineEdit、QLabel的字体、大小、颜色LineEdit的监视器pro文件p…...
wordpress html标签可以/seo排名点击软件推荐
问题描述 springboot项目在service层的业务逻辑用Transactional(rollbackFor Exception.class)注解来标明事务,结果程序报异常之后,数据依然修改成功了。。。 原因分析 因为在这块处理中,用try...catch...进行了异常的捕获,而…...
网络销售技巧/成都做整站优化
下载 该工具使用Go语言编写而成,当然为了方便不熟悉Go或者急于使用工具来解决问题的开发者,我们提供了预先编译好的各主流操作系统平台的二进制文件供大家下载使用,由于平台的多样性,我们把这些二进制打包放到一个文件里面&#x…...
按钮特效网站/河南今日重大新闻
EC20的支持以下几种工作模式,睡眠模式包括MCU主机睡眠和EC20睡眠。 当MCU主机不睡眠时,通过ATQICSK 1(发送完之后串口被禁用不再响应AT指令),DTR1(投票EC20可以进入睡眠)触发EC20进入睡眠&#…...
收费的网站如何免费/不错宁波seo公司
Microsoft Outlook虽然很多人在用,但它并不是完美的电子邮件工具。用户使用的时候依旧面临许多体验问题。以下是Microsoft Outlook的常见问题及其最佳解决方法。注意:由于市场上有各种Outlook版本,因此有多种方法可以解决常见问题。我们的解决…...