Rust面试题及详细答案120道(19-26)-- 所有权与借用

前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux… 。

前后端面试题-专栏总目录

在这里插入图片描述

文章目录

  • 一、本文面试题目录
      • 19. 简述Rust的所有权(Ownership)规则,它解决了什么问题?
      • 20. 什么是移动(Move)语义?为什么Rust默认移动而非复制?
      • 21. 哪些类型实现了`Copy` trait?`Copy`与`Clone`的区别是什么?
        • 实现`Copy` trait的类型
        • `Copy`与`Clone`的区别
      • 22. 解释借用(Borrowing)的概念,可变借用与不可变借用的规则是什么?
        • 不可变借用(`&T`)
        • 可变借用(`&mut T`)
      • 23. 为什么会出现“借用检查器(Borrow Checker)”报错?如何避免?
        • 常见报错原因
        • 避免方法
      • 24. 什么是悬垂引用(Dangling Reference)?Rust如何防止这种情况?
        • Rust如何防止悬垂引用?
      • 25. 如何理解“引用的生命周期不能长于被引用值的生命周期”?
        • 原理说明
      • 26. 举例说明同一作用域中,不可变引用与可变引用的共存限制。
        • 限制1:不可变引用存在时,不能创建可变引用
        • 限制2:可变引用存在时,不能创建不可变引用
        • 允许的情况:引用作用域分离
  • 二、120道Rust面试题目录列表

一、本文面试题目录

19. 简述Rust的所有权(Ownership)规则,它解决了什么问题?

Rust的所有权(Ownership)是管理内存的核心机制,无需垃圾回收或手动内存管理即可保证内存安全,其核心规则如下:

  1. 每个值在Rust中都有一个所有者(Owner):同一时间只能有一个所有者。
  2. 当所有者离开作用域,值会被自动释放:内存通过“作用域结束时调用drop函数”回收。
  3. 值的所有权可以转移(Move):赋值或传递参数时,所有权从原变量转移到新变量,原变量不再可用。

解决的问题

  • 内存安全问题:避免双重释放(同一内存被释放两次)和悬垂指针(引用已释放的内存)。
  • 数据竞争问题:通过单一所有者限制,避免多线程同时修改数据。
  • 性能问题:无需垃圾回收的运行时开销,内存释放时机可预测。

示例

{let s = String::from("hello");  // s是"hello"的所有者let s2 = s;                     // 所有权从s转移到s2,s不再可用// println!("{}", s);           // 编译错误:s已失去所有权
}  // s2离开作用域,"hello"被自动释放

20. 什么是移动(Move)语义?为什么Rust默认移动而非复制?

移动(Move)语义:当一个值被赋值给另一个变量(或传递给函数)时,所有权从原变量转移到新变量,原变量不再拥有该值的访问权(即“被移动”)。

示例

let v1 = vec![1, 2, 3];  // v1拥有向量的所有权
let v2 = v1;             // 向量所有权移动到v2,v1失效
// println!("{:?}", v1); // 编译错误:v1已被移动

默认移动而非复制的原因

  1. 避免双重释放:堆上的数据(如StringVec)若默认复制,会导致两个变量指向同一内存,离开作用域时双重释放。
  2. 明确内存管理:移动语义强制开发者显式处理复制(通过Clone),避免意外的内存开销。
  3. 性能优化:对于大型数据,复制操作成本高,移动仅转移所有权(类似指针传递),更高效。

注意:栈上的简单类型(如i32bool)因实现Copy trait,会默认复制而非移动(见第21题)。

21. 哪些类型实现了Copy trait?CopyClone的区别是什么?

实现Copy trait的类型

Copy trait用于标记可通过位复制(bit-for-bit copy)安全复制的类型,通常是栈上存储的简单类型:

  • 所有标量类型:i32u64f32boolchar等。
  • 包含Copy类型的元组:如(i32, bool)(若元组中所有元素都实现Copy)。
  • 不可变引用&T(但可变引用&mut T不实现Copy)。

示例

let x = 5;       // i32实现Copy
let y = x;       // 复制x的值给y,x仍可用
println!("x: {}, y: {}", x, y);  // 输出:x: 5, y: 5
CopyClone的区别
特性Copy traitClone trait
复制方式隐式的位复制(编译期自动完成)显式的自定义复制(需调用clone()方法)
适用场景简单类型(栈上数据)复杂类型(堆上数据,如StringVec
安全性必须是“无副作用”的复制可包含自定义逻辑(如深拷贝)
继承关系实现Copy必须先实现Clone实现Clone无需Copy

示例

let s1 = String::from("hello");
// let s2 = s1;  // String未实现Copy,此处为移动,s1失效
let s2 = s1.clone();  // 显式调用clone()复制,s1仍可用
println!("s1: {}, s2: {}", s1, s2);  // 输出:s1: hello, s2: hello

22. 解释借用(Borrowing)的概念,可变借用与不可变借用的规则是什么?

借用(Borrowing):允许通过引用(&T&mut T)临时访问值,而不获取所有权。引用离开作用域后,值的所有权仍归原变量。

不可变借用(&T
  • 通过&创建,允许读取值但不能修改。
  • 规则:同一作用域内,可存在多个不可变引用(只读共享)。

示例

let s = String::from("hello");
let r1 = &s;  // 不可变借用
let r2 = &s;  // 允许:多个不可变引用共存
println!("{} {}", r1, r2);  // 输出:hello hello
可变借用(&mut T
  • 通过&mut创建,允许读取和修改值。
  • 规则
    1. 同一作用域内,只能有一个可变引用(独占访问)。
    2. 可变引用与不可变引用不能同时存在(避免读写冲突)。

示例

let mut s = String::from("hello");
let r1 = &mut s;  // 可变借用
// let r2 = &mut s; // 错误:同一作用域只能有一个可变引用
r1.push_str(" world");
println!("{}", r1);  // 输出:hello world// 可变引用与不可变引用不能共存
let r3 = &s;       // 不可变引用
// let r4 = &mut s; // 错误:已有不可变引用时不能创建可变引用

借用的核心目的:在保证内存安全的前提下,实现临时访问值,避免频繁的所有权转移。

23. 为什么会出现“借用检查器(Borrow Checker)”报错?如何避免?

借用检查器(Borrow Checker) 是Rust编译器的组件,用于在编译期验证引用的合法性,确保遵循借用规则(见第22题)。若违反规则,会产生编译错误。

常见报错原因
  1. 可变引用与不可变引用共存:同一作用域内同时存在可变引用和不可变引用。
  2. 多个可变引用共存:同一作用域内存在多个可变引用。
  3. 引用生命周期长于被引用值:引用指向的值已被释放(悬垂引用)。

示例:借用检查器报错

let r;
{let x = 5;r = &x;  // 错误:x的生命周期短于r,r会成为悬垂引用
}
// println!("{}", r);
避免方法
  1. 缩小引用作用域:让引用在被引用值释放前失效。

    let x = 5;
    {let r = &x;  // r的作用域小于xprintln!("{}", r);
    }  // r失效,x仍有效
    
  2. 避免混合借用:在需要修改值时,确保没有其他引用存在。

    let mut s = String::from("hello");
    {let r1 = &s;  // 不可变引用作用域受限
    }
    let r2 = &mut s;  // 此时无其他引用,允许创建可变引用
    
  3. 显式转移所有权:若无法通过借用解决,可通过clone()复制值或转移所有权。

24. 什么是悬垂引用(Dangling Reference)?Rust如何防止这种情况?

悬垂引用(Dangling Reference):指向已被释放内存的引用,访问此类引用会导致未定义行为(如读取无效数据)。

示例:其他语言可能出现的悬垂引用

// C语言示例(不安全)
int* dangling() {int x = 5;return &x;  // x离开作用域后被释放,返回的指针成为悬垂指针
}
Rust如何防止悬垂引用?

Rust的生命周期系统借用检查器在编译期确保:

  1. 引用的生命周期不能长于被引用值的生命周期:编译器会检查引用的作用域是否在被引用值的作用域内。
  2. 值被释放前,所有引用必须失效:当值离开作用域时,其所有引用已不可访问。

Rust中的编译期阻止

fn dangle() -> &String {  // 错误:缺少生命周期标注(实际编译会更详细)let s = String::from("hello");&s  // s的生命周期仅限于函数内,返回的引用会悬垂
}

正确做法:返回值的所有权而非引用,或确保被引用值的生命周期足够长。

fn no_dangle() -> String {  // 返回所有权let s = String::from("hello");s
}

25. 如何理解“引用的生命周期不能长于被引用值的生命周期”?

“引用的生命周期不能长于被引用值的生命周期”是Rust内存安全的核心原则,可理解为:引用必须在被引用值释放前失效,确保引用始终指向有效的内存。

原理说明
  • 生命周期(Lifetime):值在内存中存在的时间段(从创建到释放)。
  • 若引用的生命周期长于被引用值,当值被释放后,引用会成为悬垂引用,导致访问无效内存。

示例:违反原则的情况

let r;                // r的生命周期开始
{let x = 5;        // x的生命周期开始r = &x;           // r引用x,但x的生命周期短于r
}                     // x的生命周期结束(被释放)
// println!("{}", r); // 错误:r的生命周期长于x,访问会导致悬垂引用

示例:遵循原则的情况

let x = 5;            // x的生命周期开始
let r = &x;           // r的生命周期开始,且短于x
println!("{}", r);    // 正确:r的生命周期在x的生命周期内
// x的生命周期结束,r的生命周期也已结束

编译器的保证:Rust通过生命周期推断和标注,在编译期确保所有引用都遵循此原则,避免运行时错误。

26. 举例说明同一作用域中,不可变引用与可变引用的共存限制。

Rust为避免数据竞争,严格限制同一作用域中不可变引用(&T)与可变引用(&mut T)的共存:不可变引用与可变引用不能同时存在,且同一时间只能有一个可变引用。

限制1:不可变引用存在时,不能创建可变引用
let mut s = String::from("hello");let r1 = &s;  // 不可变引用
let r2 = &s;  // 允许:多个不可变引用共存
// let r3 = &mut s; // 错误:已有不可变引用时,不能创建可变引用println!("{} and {}", r1, r2);  // 正确:使用不可变引用
限制2:可变引用存在时,不能创建不可变引用
let mut s = String::from("hello");let r1 = &mut s;  // 可变引用
// let r2 = &s;    // 错误:已有可变引用时,不能创建不可变引用
// let r3 = &mut s;// 错误:同一作用域只能有一个可变引用r1.push_str(" world");
println!("{}", r1);  // 正确:使用可变引用
允许的情况:引用作用域分离

若引用的作用域不重叠(通过代码块分隔),则可变引用和不可变引用可交替存在:

let mut s = String::from("hello");{let r1 = &mut s;  // 可变引用作用域限制在代码块内r1.push_str(" world");
}  // r1失效,不再有可变引用let r2 = &s;  // 此时可创建不可变引用
let r3 = &s;  // 多个不可变引用也允许
println!("{} and {}", r2, r3);  // 输出:hello world and hello world

设计目的:防止“读写冲突”和“写写冲突”,确保多线程环境下的数据安全,是Rust无数据竞争并发的基础。

二、120道Rust面试题目录列表

文章序号Rust面试题120道
1Rust面试题及详细答案120道(01-10)
2Rust面试题及详细答案120道(11-18)
3Rust面试题及详细答案120道(19-26)
4Rust面试题及详细答案120道(27-32)
5Rust面试题及详细答案120道(33-41)
6Rust面试题及详细答案120道(42-50)
7Rust面试题及详细答案120道(51-57)
8Rust面试题及详细答案120道(58-65)
9Rust面试题及详细答案120道(66-71)
10Rust面试题及详细答案120道(72-80)
11Rust面试题及详细答案120道(81-89)
12Rust面试题及详细答案120道(90-98)
13Rust面试题及详细答案120道(99-105)
14Rust面试题及详细答案120道(106-114)
15Rust面试题及详细答案120道(115-120)

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

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

相关文章

Jenkins + SonarQube 从原理到实战三:SonarQube 打通 Windows AD(LDAP)认证与踩坑记录

前言 在前两篇文章中,已经介绍了 SonarQube 的部署 以及 通过 sonar-cxx 插件实现 C/C 代码扫描。 本篇将重点讲 如何让 SonarQube 对接 Windows AD(LDAP),实现域账号登录和基于 AD 组的权限管理。 一、背景与需求分析 需求分析…

[AI React Web] 包与依赖管理 | `axios`库 | `framer-motion`库

第七章:包与依赖管理 在我们使用open-lovable的旅程中,已经探索了它如何管理对话状态(第一章:对话状态管理)、将创意转化为可运行代码(第二章:AI代码生成管道)、如何在安全的虚拟环…

PanSou 一款开源网盘搜索项目,集成前后端,一键部署,开箱即用

PanSou 网盘搜索API PanSou是一个高性能的网盘资源搜索API服务,支持TG搜索和自定义插件搜索。系统设计以性能和可扩展性为核心,支持并发搜索、结果智能排序和网盘类型分类。 项目地址:https://github.com/fish2018/pansou 特性&#xff08…

java爬虫实战

本人目前在做鱼皮的《智能协同云图库》,涉及到了以图搜图图片爬取,虽然以前有爬过图片,但是用的都是别人现成的代码,不怎么去理解为什么要这样做,这次有在尝试理解每一个步骤。本人基础极差,属于一点基础也…

深入详解C语言的循环结构:while循环、do-while循环、for循环,结合实例,讲透C语言的循环结构

🔥个人主页:艾莉丝努力练剑 ❄专栏传送门:《C语言》、《数据结构与算法》、C语言刷题12天IO强训、LeetCode代码强化刷题、C/C干货分享&学习过程记录 🍉学习方向:C/C方向 ⭐️人生格言:为天地立心&#…

北京-4年功能测试2年空窗-报培训班学测开-第七十四天-线下面试-聊的很满意但可能有风险-等信吧

今天没去教室,因为下午有个线下面试。其实是可以去教室的,但我实在太焦虑了,我觉得去了教室我心情会更不好,什么都干不下去,所以我选择不去早上依旧是带着满满焦虑起来的,会觉得自己的一切都不令自己满意&a…

在ubuntu服务器下安装cuda和cudnn(笔记)

目录 0 引言 1 相关环境查询 2 安装cuda 2.1 下载并安装 2.2 安装选项配置 2.3 验证安装 3 安装cudnn 3.1 下载 3.2 解压 3.3 删除旧版本 cuDNN 3.4 复制新文件到 CUDA 目录 3.5 设置文件权限 3.6 创建软链接 3.7 验证安装 0 引言 我在使用服务器的cuda11.8的时…

docker安装centos

docker库地址https://hub.docker.com/ 尝试使用centos7试了几次超时 换了个版本就可以了 docker pull centos:centos7.9.2009有时候需要更新资源地址 可以使用 vim /etc/docker/daemon.json配置其他资源地址 {"registry-mirrors": ["http://hub-mirror.c.163…

内容索引之word转md工具 - markitdown

切分文档构建RAG库过程中,langchain、llamaindex更期望处理latex、md类带有显式结构文档。 langchain、llamaindex切分word,有可能将段落中间截断,导致切分后的块语义不完整。 所以,需要先将word转化为md格式,然后再…

MaxKB+合合信息TextIn:通过API实现PDF扫描件的文档审核

上海合合信息科技股份有限公司(以下简称为合合信息)是一家深耕人工智能、OCR(光学字符识别)及商业大数据技术领域的科技企业。该公司拥有领先的智能文字识别技术,其名片全能王(CamCard)、扫描全…

MyBatis 核心入门:从概念到实战,一篇掌握简单增删改查

目录 一、什么是 MyBatis?为什么要用它? 二、MyBatis 核心概念(通俗理解) 1.SqlSessionFactory 2.SqlSession 3.Mapper接口 4.映射文件(XML) 三、手把手搭建第一个 MyBatis 项目 1. 准备工作 2. 核心配置文…

数据结构初阶(12)排序算法—插入排序(插入、希尔)(动图演示)

2. 常见排序算法的实现2.0 十大排序算法2.1 插入排序 2.1.1 基本思想直接插入排序是一种简单的插入排序法:基本思想把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中。直到所有的记录插入完为止,得到一个新的有序序列 。 比 挪 (…

MySQL优化常用的几个方法

本实例是对慢sql从2万优化到5千优化方法的汇总。 首先贴上优化效果:1、更新数据时使用ID更新;2、"分页/轮询"查询时先获取符合数据要求主键的最大和最小ID,然后WHERE条件增加ID步增查询;3、检查SQL是否命中WHERE条件&am…

深入解析 AUTOSAR:汽车软件开发的革命性架构

引言在汽车智能化、网联化、电动化浪潮席卷全球的今天,汽车电子系统的复杂性与日俱增。传统“烟囱式”的 ECU 开发模式(各供应商独立开发软硬件)带来了巨大的兼容性、复用性和维护成本挑战。AUTOSAR(AUTomotive Open System ARchi…

计算机视觉(opencv)实战一——图像本质、数字矩阵、RGB + 图片基本操作(灰度、裁剪、替换等)

OpenCV 入门教程: OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉库,广泛应用于图像处理、视频分析、机器学习等领域。 在 Python 中,cv2 是 OpenCV 的主要接口模块。本文将带你一步步掌握 cv2…

《探索C++ set与multiset容器:深入有序唯一性集合的实现与应用》

前引:在STL的关联式容器中,set以其严格的元素唯一性和自动排序特性成为处理有序数据的核心工具。其底层基于红黑树(Red-Black Tree)实现,保证了O(log n)的查找、插入与删除复杂度!本文将从底层原理切入&…

各测试平台功能对比分析(ITP,Postman,Apifox,MeterSphere)

对比ITP与Postman,Apifox,MeterSphere 功能特性ITPPostmanApifoxMeterSphere接口测试✅ 可视化接口调试,支持多种请求方式✅ 支持✅ 支持✅ 支持场景测试✅ 多接口串联测试,支持前后置脚本✅ Collections功能✅ 支持✅ 支持定时任务✅ 基于Celery的定时…

开源日志log4cplus—如何将 string类型转为tstring类型,又如何将char*类型转换为tstring类型?

文章目录🔧 一、理解 log4cplus::tstring 的本质⚙️ 二、std::string 转 tstring 的三种方法✅ 1. 使用内置宏 LOG4CPLUS_STRING_TO_TSTRING(推荐)✅ 2. 手动条件编译转换(精细控制)✅ 3. 多字节模式下的直接赋值⚙️…

深度学习之CNN网络简介

CNN网络简单介绍 1.概述 卷积神经网络(Convolutional Neural Network,CNN)是一种专门用于处理具有网格状结构数据的深度学习模型。 ​ CNN网络主要有三部分构成:卷积层、池化层和全连接层构成,其中卷积层负责提取图像中…

【微实验】基频提取的MATLAB实现(优化版)

前情提要: 【超详细】科普:别再只会用自相关!YIN 和 PYIN 如何破解音频隐藏密码?-CSDN博客 【微实验】妈妈我的MATLAB会识别声音的基频了!-CSDN博客 今天用MATLAB把算法封装成函数,然后调用对比结果。 …