Rust编程-核心篇-不安全编程_rust安全性
zhezhongyun 2025-10-23 14:16 8 浏览
Unsafe的必要性
Rust的所有权系统和类型系统为我们提供了强大的安全保障,但在某些情况下,我们需要突破这些限制来:
- 与C代码交互
- 实现底层系统编程
- 优化性能关键代码
- 实现某些编译器无法验证的安全操作
Rust的unsafe关键字不是要我们放弃安全性,而是让我们在明确标记的边界内,承担起确保代码安全的责任。unsafe代码块是Rust安全模型的重要组成部分,它们让我们能够构建安全的抽象。
Unsafe的基础概念
什么是Unsafe?
unsafe fn dangerous_function() {
// 这里可以执行不安全的操作
}
unsafe {
// 不安全的代码块
dangerous_function();
}
Unsafe的五个超能力
- 解引用裸指针
- 调用unsafe函数或方法
- 访问或修改可变静态变量
- 实现unsafe trait
- 访问union的字段
裸指针
创建和使用裸指针
fn main() {
let mut num = 5;
// 创建不可变裸指针
let r1 = &num as *const i32;
// 创建可变裸指针
let r2 = &mut num as *mut i32;
unsafe {
println!("r1 is: {}", *r1);
println!("r2 is: {}", *r2);
// 修改通过裸指针指向的值
*r2 = 10;
println!("After modification: {}", *r1);
}
}
裸指针的实际应用:实现自定义智能指针
use std::ops::Deref;
use std::ops::DerefMut;
struct MyBox<T> {
ptr: *mut T,
}
impl<T> MyBox<T> {
fn new(value: T) -> Self {
let boxed = Box::new(value);
let ptr = Box::into_raw(boxed);
MyBox { ptr }
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.ptr }
}
}
impl<T> DerefMut for MyBox<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.ptr }
}
}
impl<T> Drop for MyBox<T> {
fn drop(&mut self) {
unsafe {
let _ = Box::from_raw(self.ptr);
}
}
}
fn main() {
let mut my_box = MyBox::new(42);
println!("Value: {}", *my_box);
*my_box = 100;
println!("New value: {}", *my_box);
}
调用Unsafe函数
标准库中的Unsafe函数
use std::slice;
fn main() {
let mut v = vec![1, 2, 3, 4, 5, 6];
let r = &mut v[..];
let (a, b) = r.split_at_mut(3);
assert_eq!(a, &mut [1, 2, 3]);
assert_eq!(b, &mut [4, 5, 6]);
}
// 手动实现split_at_mut
fn split_at_mut(values: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
let len = values.len();
let ptr = values.as_mut_ptr();
assert!(mid <= len);
unsafe {
(
slice::from_raw_parts_mut(ptr, mid),
slice::from_raw_parts_mut(ptr.add(mid), len - mid),
)
}
}
实现Unsafe函数
use std::slice;
unsafe fn dangerous_function(ptr: *const i32, len: usize) -> &'static [i32] {
slice::from_raw_parts(ptr, len)
}
fn main() {
let mut vector = vec![1, 2, 3, 4, 5];
let (left, right) = vector.split_at_mut(2);
unsafe {
let slice = dangerous_function(left.as_ptr(), left.len());
println!("Slice: {:?}", slice);
}
}
可变静态变量
全局可变状态
static mut COUNTER: u32 = 0;
fn add_to_count(inc: u32) {
unsafe {
COUNTER += inc;
}
}
fn main() {
add_to_count(3);
unsafe {
println!("COUNTER: {}", COUNTER);
}
}
线程安全的全局状态
use std::sync::atomic::{AtomicUsize, Ordering};
static COUNTER: AtomicUsize = AtomicUsize::new(0);
fn add_to_count(inc: usize) {
COUNTER.fetch_add(inc, Ordering::SeqCst);
}
fn main() {
add_to_count(3);
println!("COUNTER: {}", COUNTER.load(Ordering::SeqCst));
}
Unsafe Trait
实现Unsafe Trait
unsafe trait UnsafeTrait {
fn dangerous_method(&self);
}
unsafe impl UnsafeTrait for i32 {
fn dangerous_method(&self) {
println!("Dangerous method called on: {}", self);
}
}
fn main() {
let x = 42;
unsafe {
x.dangerous_method();
}
}
实际应用:Send和Sync
use std::thread;
struct MyStruct {
data: *mut i32,
}
// 手动实现Send和Sync
unsafe impl Send for MyStruct {}
unsafe impl Sync for MyStruct {}
impl MyStruct {
fn new(value: i32) -> Self {
let boxed = Box::new(value);
let ptr = Box::into_raw(boxed);
MyStruct { data: ptr }
}
fn get(&self) -> i32 {
unsafe { *self.data }
}
fn set(&self, value: i32) {
unsafe { *self.data = value; }
}
}
impl Drop for MyStruct {
fn drop(&mut self) {
unsafe {
let _ = Box::from_raw(self.data);
}
}
}
fn main() {
let my_struct = MyStruct::new(42);
let handle = thread::spawn(move || {
println!("Value: {}", my_struct.get());
my_struct.set(100);
println!("New value: {}", my_struct.get());
});
handle.join().unwrap();
}
实际应用
use std::alloc::{alloc, dealloc, Layout};
use std::ptr::NonNull;
struct MemoryPool {
blocks: Vec<NonNull<u8>>,
layout: Layout,
block_size: usize,
}
impl MemoryPool {
fn new(block_size: usize, initial_capacity: usize) -> Self {
let layout = Layout::from_size_align(block_size, 8).unwrap();
let mut blocks = Vec::with_capacity(initial_capacity);
// 预分配一些内存块
for _ in 0..initial_capacity {
unsafe {
let ptr = alloc(layout);
if ptr.is_null() {
panic!("Failed to allocate memory");
}
blocks.push(NonNull::new_unchecked(ptr));
}
}
Self {
blocks,
layout,
block_size,
}
}
fn allocate(&mut self) -> Option<NonNull<u8>> {
self.blocks.pop()
}
fn deallocate(&mut self, ptr: NonNull<u8>) {
self.blocks.push(ptr);
}
}
impl Drop for MemoryPool {
fn drop(&mut self) {
for block in &self.blocks {
unsafe {
dealloc(block.as_ptr(), self.layout);
}
}
}
}
fn main() {
let mut pool = MemoryPool::new(1024, 10);
if let Some(ptr) = pool.allocate() {
unsafe {
// 使用分配的内存
let slice = std::slice::from_raw_parts_mut(ptr.as_ptr(), 1024);
slice[0] = 42;
println!("Allocated memory, first byte: {}", slice[0]);
}
// 归还内存
pool.deallocate(ptr);
}
}
FFI接口
use std::ffi::{CString, CStr};
use std::os::raw::c_char;
// 声明外部C函数
extern "C" {
fn strlen(s: *const c_char) -> usize;
fn strcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char;
}
// 安全的Rust包装
pub fn safe_strlen(s: &str) -> usize {
let c_string = CString::new(s).unwrap();
unsafe { strlen(c_string.as_ptr()) }
}
pub fn safe_strcpy(dst: &mut [u8], src: &str) -> Result<(), String> {
if dst.len() < src.len() + 1 {
return Err("Destination buffer too small".to_string());
}
let c_src = CString::new(src).map_err(|_| "Invalid string")?;
unsafe {
strcpy(dst.as_mut_ptr() as *mut c_char, c_src.as_ptr());
}
Ok(())
}
fn main() {
let s = "Hello, World!";
println!("Length: {}", safe_strlen(s));
let mut buffer = vec![0u8; 20];
safe_strcpy(&mut buffer, s).unwrap();
let result = unsafe {
CStr::from_ptr(buffer.as_ptr() as *const c_char)
.to_string_lossy()
};
println!("Copied string: {}", result);
}
零拷贝解析
use std::mem;
#[repr(C)]
struct Header {
magic: u32,
version: u16,
length: u16,
}
impl Header {
fn from_bytes(bytes: &[u8]) -> Result<&Self, &'static str> {
if bytes.len() < mem::size_of::<Self>() {
return Err("Insufficient data");
}
unsafe {
let ptr = bytes.as_ptr() as *const Self;
Ok(&*ptr)
}
}
fn from_bytes_mut(bytes: &mut [u8]) -> Result<&mut Self, &'static str> {
if bytes.len() < mem::size_of::<Self>() {
return Err("Insufficient data");
}
unsafe {
let ptr = bytes.as_mut_ptr() as *mut Self;
Ok(&mut *ptr)
}
}
}
fn main() {
let mut data = vec![0u8; 8];
// 设置头部数据
if let Ok(header) = Header::from_bytes_mut(&mut data) {
header.magic = 0x12345678;
header.version = 1;
header.length = 8;
}
// 读取头部数据
if let Ok(header) = Header::from_bytes(&data) {
println!("Magic: 0x{:x}", header.magic);
println!("Version: {}", header.version);
println!("Length: {}", header.length);
}
}
安全抽象
use std::ptr;
pub struct SafeVector<T> {
ptr: *mut T,
len: usize,
capacity: usize,
}
impl<T> SafeVector<T> {
pub fn new() -> Self {
Self {
ptr: ptr::null_mut(),
len: 0,
capacity: 0,
}
}
pub fn push(&mut self, value: T) {
if self.len == self.capacity {
self.grow();
}
unsafe {
let end = self.ptr.add(self.len);
ptr::write(end, value);
}
self.len += 1;
}
pub fn pop(&mut self) -> Option<T> {
if self.len == 0 {
None
} else {
self.len -= 1;
unsafe {
let end = self.ptr.add(self.len);
Some(ptr::read(end))
}
}
}
pub fn get(&self, index: usize) -> Option<&T> {
if index < self.len {
unsafe {
Some(&*self.ptr.add(index))
}
} else {
None
}
}
fn grow(&mut self) {
let new_capacity = if self.capacity == 0 { 1 } else { self.capacity * 2 };
let new_size = new_capacity * std::mem::size_of::<T>();
unsafe {
let new_ptr = if self.capacity == 0 {
std::alloc::alloc(std::alloc::Layout::from_size_align(new_size, std::mem::align_of::<T>()).unwrap())
} else {
std::alloc::realloc(
self.ptr as *mut u8,
std::alloc::Layout::from_size_align(self.capacity * std::mem::size_of::<T>(), std::mem::align_of::<T>()).unwrap(),
new_size
)
};
if new_ptr.is_null() {
panic!("Failed to allocate memory");
}
self.ptr = new_ptr as *mut T;
self.capacity = new_capacity;
}
}
}
impl<T> Drop for SafeVector<T> {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe {
// 调用所有元素的析构函数
for i in 0..self.len {
ptr::drop_in_place(self.ptr.add(i));
}
// 释放内存
std::alloc::dealloc(
self.ptr as *mut u8,
std::alloc::Layout::from_size_align(self.capacity * std::mem::size_of::<T>(), std::mem::align_of::<T>()).unwrap()
);
}
}
}
}
fn main() {
let mut vec = SafeVector::new();
vec.push(1);
vec.push(2);
vec.push(3);
println!("Length: {}", vec.len);
println!("First element: {:?}", vec.get(0));
println!("Last element: {:?}", vec.get(2));
while let Some(value) = vec.pop() {
println!("Popped: {}", value);
}
}
性能优化
避免边界检查
fn safe_sum(slice: &[i32]) -> i32 {
slice.iter().sum()
}
fn unsafe_sum(slice: &[i32]) -> i32 {
let mut sum = 0;
let len = slice.len();
unsafe {
let ptr = slice.as_ptr();
for i in 0..len {
sum += *ptr.add(i);
}
}
sum
}
fn main() {
let data = vec![1, 2, 3, 4, 5];
println!("Safe sum: {}", safe_sum(&data));
println!("Unsafe sum: {}", unsafe_sum(&data));
}
常见陷阱与最佳实践
1. 避免悬垂指针
// 错误的做法
fn bad_function() -> &'static i32 {
let x = 42;
&x // 返回局部变量的引用
}
// 正确的做法
fn good_function() -> i32 {
let x = 42;
x // 返回值而不是引用
}
2. 确保内存对齐
use std::mem;
#[repr(align(16))]
struct AlignedStruct {
data: [u8; 16],
}
fn main() {
let aligned = AlignedStruct { data: [0; 16] };
println!("Alignment: {}", mem::align_of_val(&aligned));
}
3. 使用工具检查
// 使用Miri检查未定义行为
// cargo +nightly miri run
// 使用AddressSanitizer
// RUSTFLAGS="-Z sanitizer=address" cargo run
写在最后
Rust的unsafe编程提供了:
- 底层控制:直接操作内存和硬件
- 性能优化:避免不必要的安全检查
- FFI支持:与C代码交互
- 安全抽象:构建安全的API
Unsafe不是Rust的缺陷,而是其设计的重要组成部分。通过unsafe,我们可以在保持Rust安全模型的同时,获得底层编程的能力。关键是要将unsafe代码限制在最小的范围内,并通过安全的接口暴露功能。
相关推荐
- Python入门学习记录之一:变量_python怎么用变量
-
写这个,主要是对自己学习python知识的一个总结,也是加深自己的印象。变量(英文:variable),也叫标识符。在python中,变量的命名规则有以下三点:>变量名只能包含字母、数字和下划线...
- python变量命名规则——来自小白的总结
-
python是一个动态编译类编程语言,所以程序在运行前不需要如C语言的先行编译动作,因此也只有在程序运行过程中才能发现程序的问题。基于此,python的变量就有一定的命名规范。python作为当前热门...
- Python入门学习教程:第 2 章 变量与数据类型
-
2.1什么是变量?在编程中,变量就像一个存放数据的容器,它可以存储各种信息,并且这些信息可以被读取和修改。想象一下,变量就如同我们生活中的盒子,你可以把东西放进去,也可以随时拿出来看看,甚至可以换成...
- 绘制学术论文中的“三线表”具体指导
-
在科研过程中,大家用到最多的可能就是“三线表”。“三线表”,一般主要由三条横线构成,当然在变量名栏里也可以拆分单元格,出现更多的线。更重要的是,“三线表”也是一种数据记录规范,以“三线表”形式记录的数...
- Python基础语法知识--变量和数据类型
-
学习Python中的变量和数据类型至关重要,因为它们构成了Python编程的基石。以下是帮助您了解Python中的变量和数据类型的分步指南:1.变量:变量在Python中用于存储数据值。它们充...
- 一文搞懂 Python 中的所有标点符号
-
反引号`无任何作用。传说Python3中它被移除是因为和单引号字符'太相似。波浪号~(按位取反符号)~被称为取反或补码运算符。它放在我们想要取反的对象前面。如果放在一个整数n...
- Python变量类型和运算符_python中变量的含义
-
别再被小名词坑哭了:Python新手常犯的那些隐蔽错误,我用同事的真实bug拆给你看我记得有一次和同事张姐一起追查一个看似随机崩溃的脚本,最后发现罪魁祸首竟然是她把变量命名成了list。说实话...
- 从零开始:深入剖析 Spring Boot3 中配置文件的加载顺序
-
在当今的互联网软件开发领域,SpringBoot无疑是最为热门和广泛应用的框架之一。它以其强大的功能、便捷的开发体验,极大地提升了开发效率,成为众多开发者构建Web应用程序的首选。而在Spr...
- Python中下划线 ‘_’ 的用法,你知道几种
-
Python中下划线()是一个有特殊含义和用途的符号,它可以用来表示以下几种情况:1在解释器中,下划线(_)表示上一个表达式的值,可以用来进行快速计算或测试。例如:>>>2+...
- 解锁Shell编程:变量_shell $变量
-
引言:开启Shell编程大门Shell作为用户与Linux内核之间的桥梁,为我们提供了强大的命令行交互方式。它不仅能执行简单的文件操作、进程管理,还能通过编写脚本实现复杂的自动化任务。无论是...
- 一文学会Python的变量命名规则!_python的变量命名有哪些要求
-
目录1.变量的命名原则3.内置函数尽量不要做变量4.删除变量和垃圾回收机制5.结语1.变量的命名原则①由英文字母、_(下划线)、或中文开头②变量名称只能由英文字母、数字、下画线或中文字所组成。③英文字...
- 更可靠的Rust-语法篇-区分语句/表达式,略览if/loop/while/for
-
src/main.rs://函数定义fnadd(a:i32,b:i32)->i32{a+b//末尾表达式}fnmain(){leta:i3...
- C++第五课:变量的命名规则_c++中变量的命名规则
-
变量的命名不是想怎么起就怎么起的,而是有一套固定的规则的。具体规则:1.名字要合法:变量名必须是由字母、数字或下划线组成。例如:a,a1,a_1。2.开头不能是数字。例如:可以a1,但不能起1a。3....
- Rust编程-核心篇-不安全编程_rust安全性
-
Unsafe的必要性Rust的所有权系统和类型系统为我们提供了强大的安全保障,但在某些情况下,我们需要突破这些限制来:与C代码交互实现底层系统编程优化性能关键代码实现某些编译器无法验证的安全操作Rus...
- 探秘 Python 内存管理:背后的神奇机制
-
在编程的世界里,内存管理就如同幕后的精密操控者,确保程序的高效运行。Python作为一种广泛使用的编程语言,其内存管理机制既巧妙又复杂,为开发者们提供了便利的同时,也展现了强大的底层控制能力。一、P...
- 一周热门
- 最近发表
- 标签列表
-
- HTML 教程 (33)
- HTML 简介 (35)
- HTML 实例/测验 (32)
- HTML 测验 (32)
- JavaScript 和 HTML DOM 参考手册 (32)
- HTML 拓展阅读 (30)
- HTML文本框样式 (31)
- HTML滚动条样式 (34)
- HTML5 浏览器支持 (33)
- HTML5 新元素 (33)
- HTML5 WebSocket (30)
- HTML5 代码规范 (32)
- HTML5 标签 (717)
- HTML5 标签 (已废弃) (75)
- HTML5电子书 (32)
- HTML5开发工具 (34)
- HTML5小游戏源码 (34)
- HTML5模板下载 (30)
- HTTP 状态消息 (33)
- HTTP 方法:GET 对比 POST (33)
- 键盘快捷键 (35)
- 标签 (226)
- opacity 属性 (32)
- transition 属性 (33)
- 1-1. 变量声明 (31)
