Rust常用特型之Clone+Copy特型

Rust常用特型之Clone+Copy特型。在Rust标准库中,存在很多常用的工具类特型,它们能帮助我们写出更具有Rust风格的代码。

今天,我们把Clone 和 Copy 特型放在一起学习。

(注:本文更多的是对《Programing Rust 2nd Edition》的自己翻译和理解,并不是原创)

一 什么是Clone

标准库的Clone特型意味着实现它的类型可以制作一份值的副本。它的定义如下:

trait Clone: Sized {fn clone(&self) -> Self;fn clone_from(&mut self, source: &Self) {*self = source.clone()}
}

它有两个函数,其中一个函数有默认实现,因此我们只需要自己实现第一个函数clone()就好,实际上,运用的最多的也是clone()函数。

clone()函数会构造自身的独立备份并且返回它。因为该函数的返回类似是Self, 因此函数不可能返回unsized值。这是因为Clone特型本身拓展了Sized特型,因此,实现了Clone特型的类型必须是Sized

克隆一个值通常需要为它所拥有的任何内容重新分配资源,所以克隆在时间和内存上都开销很大。例如, 克隆一个Vec<String> 不仅仅复制了向量本身,也复制了它的每一个字符串元素。这就是为什么Rust不自动实现克隆,而需要你手动显式调用的原因。引用计数指针类型例如Rc<T>andArc<T>是个例外,克隆他们只是简单的增加一个引用计数,并且返回给你一个新的指针。

clone_from函数从一个源值复制,它的默认实现很简单,只是调用了source的clone函数。因为函数参数self的类型是&mut,因此这里只需要重新赋值即可*self = value。这样做是没有问题的,但是某些场景下,有更快的方法来达到同样的效果。

例如 假定st都是String,语句s = t.clone() 它首先克隆t,然后drops的旧值,然后移动新复制的值到s。这里涉及到堆的分配和释放两种操作。但是如果指向最开始s的堆缓冲区有足够的容量来容纳t的元素,那么我们就不需要重新分配或者释放堆。你只是简单复制t的文本到s的缓冲区并修改长度即可。

在通用代码中,您应该尽量使用clone_from以利用可能存在的优化。这里我前面说错了,我们常用clone函数,但是这里更推荐clone_from函数,虽然它可能会多拼一点.这里的真实意思其实是这样的

我们首先看clone_from的注释(vscode中的tooltips):

Performs copy-assignment from source.

a.clone_from(&b) is equivalent to a = b.clone() in functionality, but can be overridden to reuse the resources of a to avoid unnecessary allocations.

上面解释的很清楚了,这里再说一下,有下面两种情况:

  1. s 之前未定义或者未被初始化过 ,此时s无法转换为self,因为不存在或者未初始化,只能使用s = t.clone()

  2. s 之前初始化过,需要重新赋值,这时可以使用 clone_from以便可能进行优化。

    let mut s = "Hello World".to_owned();
    let t = "Hello Hi".to_owned();
    s.clone_from(&t);
    // Clone::clone_from(&mut s, &t); // 这种方式也是可行的
    // String::clone_from(&mut s, &t); // 这种方式也是可行的
    println!("s: {s}");
    

    这里也可以看出特型的几种调用方式:

    1. 值调用
    2. 特型名称调用
    3. 类型名称调用

    其中 2和3 相当于类的静态方法,1相当于类的实例上的方法

如果你的克隆实现只是简单的复制你的类型中的每个字段或者元素,并且默认的clone_from也很适用,那么Rust可以自动帮你实现Clone特型,你只需要在你的类型定义上面加上#[derive(Clone)]就行。

在标准库中,几乎所有有确切复制含义的类型都实现了Clone特型。例如像booli32这样的元数据类型实现了Clone,像StringVec<T>HasMap等容器类型也实现了Clone。有些类型没有明确的复制含义,例如std::sync::Mutex,它们没有实现Clone特型。一些类型可以复制,但是复制可能失败(操作系统有可能无法拥有必需的资源),例如std::fs::File类型,也没有实现Clone,因为clone函数是不能失败的。 作为替代方案,std::fs::File提供了一个try_clone函数,它返回一个Result<File>,它可以用来处理失败情况。

二 Copy

Clone是复制,Copy也是复制,那么很多人肯定和我一样有疑惑,这两者有什么区别呢?不急,我们先看书中怎么说的。

对于绝大多数类型来说,赋值是move值,而不是复制他们。移动一个值使得追踪他们拥有的资源更简单。但是也有例外,不拥有任何资源的类型可以是Copy类型,此时赋值仅是对源值的复制,并不会移动源值并让原变量变成未初始化状态。

Copy类型是指实现了std::marker::Copy特型的类型,该特型定义如下:

trait Copy: Clone { }

可以看出,定义非常简单,Copy特型只是简单扩展了一下Clone,并没有定义新的方法。也就是说,Copy类型一定是Clone的,而Clone类型却未必是Copy的。例如String,它是Clone的,但是不是Copy的。 u32类型,上面提到过,它是Clone的,同时也是Copy的。

那么Copy类型为什么也要是Clone类型呢?因为复制行为必须依赖Clone实现。

在Rust中,这种简单特型扩展的用法很常见,你可以利用它从一类类型中(Clone类型)再划出一个小类型(Copy类型)。

如果想对自己的类型实现Copy特型,只需要这么做

impl Copy for MyType { }

因为Copy特型是一个标记类型,对Rust语言来说有特殊的含义,因此,Rust中Copy类型的复制行为只能是字节到字节的复制。如果类型拥有其它资源,例如堆缓冲区和操作系统句柄,他们是不能实现Copy类型的。

(那么这个约束是怎么强制的?应该是编译器检查的)

在学习Drop特型的时候提到,任何实现了Drop特型的类型不能再是Copy类型,Rust会假定如果一个类型需要特殊的清理逻辑,那么它必定需要特殊的复制机制,因此不能实现Copy

Clone一样,你可以让Rust自动为你复现Copy特型,方式为使用#[derive(Copy)]语法。你也会经常看到一次性的derive两个特型:#[derive(Copy, Clone)] 。这两种方式的区别是,如果使用方式一,那么先必须为该类型实现了Clone特型,如果使用方式二,则一次性完整,大多数情况下使用方式二。

// 方式一
#[derive(Copy)]
struct MyType;impl Clone for MyType {fn clone(&self) -> Self {Self {  }}
}// 方式二#[derive(Clone, Copy)]
struct MyType;

记住,你不能为String实现Copy特型,因为两者都是外部的。一个结构体,只有所有字段是Copy特型时才能是可Copy的,例如

#[derive(Clone, Copy,Debug)]
struct MyType {name:String
}

这段代码会提示你该类型无法复制Copy。我们把name字段换一下就可以了,如下:

#[derive(Clone, Copy,Debug)]
struct MyType {age:u32
}

对于上面的MyType,我们运行如下代码片断:

let a = MyType {age:10};
let b = a;
println!("{a:?},{b:?}");

会输出MyType { age: 10 },MyType { age: 10 }。 这里可见发生了Copy而不是Move

注意:

谨慎考虑将一个类型设计为Copy类型,虽然这么做使用该类型会比较简单,但是对它的实现有比较大的限制。并且,复制是比较昂贵的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/784925.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

类加载过程详解

1、加载 通过类的全名&#xff0c;获取类的二进制数据流。解析类的二进制数据流为方法区内的数据结构&#xff08;Java类模型&#xff09; 创建java.lang.Class类的实例&#xff0c;表示该类型。作为方法区这个类的各种数据的访问入口 2、验证 验证类是否符合JVM规范&…

Altair® Embed® 嵌入式系统的可视化环境

Altair Embed 嵌入式系统的可视化环境 Altair Embed 是一款成熟的工具&#xff0c;可从框图模型自动生成代码并将其传输到通用控制器硬件来开发嵌入式系统。 基于系统关系图创建工作仿真后&#xff0c;用户即可对硬件控制器自动生成相关代码&#xff0c;并通过硬件在环 (HIL)…

ubuntu 安装配置samba服务器完整教程

ubuntu 安装配置samba服务器完整教程 问题描述解决方法郑重声明:本人原创博文,都是实战,均经过实际项目验证出货的 转载请标明出处:攻城狮2015 Platform: Intel arm64 OS:ubuntu16.04 问题描述 在安卓驱动系统开发的过程中,会需要打架服务器,又需要搭建samba服务器,下面就…

【BlossomConfig】SpringCloud项目是如何对bootstrap配置文件进行加载的?

文章目录 bootstrap配置文件的读取 网关项目源码 RPC项目源码 配置中心项目源码 bootstrap配置文件的读取 我们首先来了解一下springboot是如何做配置管理的。 了解了springboot对配置文件的管理&#xff0c;我们就能知道为什么springcloud类型的项目要使用bootstrap配置文件了…

美国RAKsmart:裸机云站群服务器配置详解

裸机云&#xff0c;也称为物理云&#xff0c;是一种云服务模式&#xff0c;它为用户提供了接近物理机性能的云服务器。而站群服务器&#xff0c;则是为了支持多个网站或应用程序的运行而设计的服务器。美国RAKsmart作为一家知名的云服务提供商&#xff0c;其裸机云站群服务器配…

【大模型】大模型发展历程,从GPT开始到Grok-1.5霸榜

目录 大模型发展历程 大模型发展历程 2018年6月&#xff0c;openAI发布GPT1&#xff1b;2019年2月&#xff0c;发布GPT2&#xff1b;2020年6月&#xff0c;发布GPT-3。 2022年11月30日&#xff0c;openAI发布ChatGPT。 2023年3月14日&#xff0c;GPT-4 发布。 2023年7月18日…

Proteus 12V to 5V buck电路仿真练习及遇到的一些问题汇总

基础电路仿真实验记录贴&#xff01;&#xff01;&#xff01;如有写的不对的地方欢迎交流指正&#xff01;&#xff01;&#xff01; 平台&#xff1a;PC win10 软件&#xff1a;Proteus8.10 仿真目标&#xff1a;buck降压电路&#xff08;PWM控制输出电压&#xff09; 写在…

八皇后问题解决过程字符串可视化

查找到问题&#xff0c;暂停600毫秒&#xff0c; 穷举本行&#xff0c;200毫秒 返回上一层之前&#xff0c;会弹出回滚上一层&#xff08;4&#xff0c;X&#xff09;&#xff0c;并暂停600毫秒 成功返回时&#xff0c;会弹出上一层具体数据如&#xff08;4&#xff0c;3&a…

Java | Leetcode Java题解之第3题无重复字符的最长子串

题目&#xff1a; 题解&#xff1a; class Solution {public int lengthOfLongestSubstring(String s) {// 哈希集合&#xff0c;记录每个字符是否出现过Set<Character> occ new HashSet<Character>();int n s.length();// 右指针&#xff0c;初始值为 -1&#…

分治——归并排序算法

例题一 解法&#xff08;归并排序&#xff09;&#xff1a; 算法思路&#xff1a; 归并排序的流程充分的体现了「分⽽治之」的思想&#xff0c;⼤体过程分为两步&#xff1a; ◦ 分&#xff1a;将数组⼀分为⼆为两部分&#xff0c;⼀直分解到数组的⻓度为 1 &#xff0c;使…

【MySQL】DQL-分组查询-语法&where与having的区别&注意事项&可cv例题语句

前言 大家好吖&#xff0c;欢迎来到 YY 滴MySQL系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C Linux的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

【Web应用技术基础】CSS(6)——使用 HTML/CSS 实现 Educoder 顶部导航栏

第一题&#xff1a;使用flex布局实现Educoder顶部导航栏容器布局 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Educoder</title><script src"https://cdn.staticfile.org/jquery/1.1…

EDM营销文案的撰写技巧?如何提升转化率?

EDM营销文案与SEO策略如何结合&#xff1f;怎么写有效营销邮件&#xff1f; EDM仍然是一种高效且经济的推广手段。撰写优质的EDM营销文案&#xff0c;不仅可以提升品牌形象&#xff0c;还能有效促进销售转化。那么&#xff0c;如何撰写出引人入胜的EDM营销文案呢&#xff1f;A…

Anaconda conda常用命令

查看配置信息&#xff1a;conda config --show 恢复默认源&#xff1a;conda config --remove-key channels 创建虚拟环境&#xff1a;conda create -n env_name python3.8&#xff0c;表示创建python版本为3.8、名字为env_name的虚拟环境。 查看虚拟环境&#xff1a;conda …

Python点云处理(二十二)基于XGBoost的点云分类算法

目录 0 简述1 XGBoost2 点云特征向量构建3 用XGBoost进行分类4 代码实现5 结果展示0 简述 点云分类是计算机视觉领域中的一个重要任务,其主要目标是将三维空间中的点云数据划分为不同的类别。点云是由大量的三维点组成的数据集,通常由激光雷达或结构光等传感器采集而来。点云…

KNN算法 | K近邻:KD Tree、球树、KNN数据下采样策略

目录 一. KNN算法实现方式1. 蛮力实现(brute)2. KD树(kd_tree)3. 球树(ball_tree) 二. KD Tree算法1. 构建方式2. KD Tree 查找最近邻 三. 球树(Ball Tree)1. 构建方式 四. KNN评价1. 优点2. 缺点 五. 延申1. KNN数据下采样策略策略1策略2策略3策略4 Condensed Nearest Neighbo…

Leo赠书活动-23期 【Python数据分析】文末送书

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 赠书活动专栏 ✨特色专栏&#xff1a;…

大模型遇上心理健康咨询:MeChat、QiaoBan、SoulChat、MindChat四大心理健康领域微调模型总结

HealthX AI 2023-09-18 08:05 江苏 以下文章来源于老刘说NLP &#xff0c;作者刘焕勇 老刘说NLP. 老刘&#xff0c;NLP开源爱好者与践行者。主页&#xff1a;https://liuhuanyong.github.io。老刘说NLP&#xff0c;将定期发布语言资源、工程实践、技术总结等内容&#xff0c;…

C语言 06 无符号数

所有的数据底层都是采用二进制来进行保存的。 第一位用于保存符号位。 如果不考虑这个符号位&#xff0c;那么所有的数都是按照正数来表示。 比如考虑了符号位的 char 类型&#xff1a; 考虑符号表示范围&#xff1a;-128 ~ 127不考虑符号&#xff1a;0 ~ 255 直接使用这些不…

SublimeText3多次保存自动弹出窗口

文章目录 1. 取消自动更新2. 阻止程序与服务器进行通信3. 尝试保存&#xff0c;弹出不出现 1. 取消自动更新 在settings中加入&#xff1a; "update_check": false2. 阻止程序与服务器进行通信 在/etc/hosts&#xff08;Linux/macOS&#xff09;或C:\Windows\Syst…