当前位置: 首页 > article >正文

Rust从入门到精通之精通篇:22.Unsafe Rust 详解

Unsafe Rust 详解

在 Rust 的设计哲学中,安全性是核心原则之一。Rust 的所有权系统、借用检查器和类型系统共同保证了内存安全和线程安全。然而,有些底层操作无法通过 Rust 的安全检查机制进行验证,这就是 unsafe Rust 存在的原因。在本章中,我们将深入探讨 unsafe Rust,了解它的使用场景、原理和最佳实践。

为什么需要 Unsafe Rust

安全抽象的基石

Rust 的许多安全抽象实际上是建立在 unsafe 代码之上的。例如,标准库中的 VecStringBox 等类型内部都使用了 unsafe 代码来实现高效的内存管理。

与外部代码交互

当 Rust 需要与其他语言(如 C/C++)编写的代码交互时,通常需要使用 unsafe 代码来桥接不同语言的边界。

性能优化

在某些性能关键的场景中,安全 Rust 的限制可能导致性能损失。通过谨慎使用 unsafe 代码,可以实现更高效的实现。

Unsafe 能力

unsafe 关键字允许你执行以下五种在安全 Rust 中被禁止的操作:

  1. 解引用裸指针
  2. 调用 unsafe 函数或方法
  3. 访问或修改可变静态变量
  4. 实现 unsafe trait
  5. 访问 union 的字段

让我们逐一探讨这些能力。

1. 解引用裸指针

Rust 提供了两种裸指针类型:*const T*mut T,分别对应不可变和可变引用。与引用不同,裸指针:

  • 可以为空
  • 不保证指向有效内存
  • 可以存在多个指向同一位置的可变裸指针
  • 没有生命周期检查
  • 不实现自动清理
fn main() {let mut num = 5;// 创建裸指针是安全的let r1 = &num as *const i32;let r2 = &mut num as *mut i32;// 解引用裸指针需要 unsafe 块unsafe {println!("r1 指向的值: {}", *r1);*r2 = 10;println!("修改后的值: {}", *r1);}
}
裸指针的实际应用

裸指针在以下场景中特别有用:

  1. 与 C 代码交互:C API 通常使用指针传递数据
use std::ffi::c_void;extern "C" {fn some_c_function(data: *mut c_void);
}fn main() {let mut data = 42;unsafe {some_c_function(&mut data as *mut _ as *mut c_void);}
}
  1. 实现自定义数据结构:某些高级数据结构(如无锁数据结构)需要精确控制内存
struct MyBox<T> {ptr: *mut T,
}impl<T> MyBox<T> {fn new(x: T) -> Self {let ptr = Box::into_raw(Box::new(x));MyBox { ptr }}
}impl<T> std::ops::Deref for MyBox<T> {type Target = T;fn deref(&self) -> &Self::Target {unsafe { &*self.ptr }}
}impl<T> Drop for MyBox<T> {fn drop(&mut self) {unsafe {Box::from_raw(self.ptr);}}
}

2. 调用 Unsafe 函数或方法

unsafe 函数是那些在调用时需要满足特定条件,但编译器无法验证这些条件的函数。调用这些函数需要使用 unsafe 块,表明调用者已经确保满足所有必要条件。

// 声明一个 unsafe 函数
unsafe fn dangerous() {println!("这是一个危险操作");// 可能包含不安全操作
}fn main() {// 调用 unsafe 函数需要 unsafe 块unsafe {dangerous();}
}
创建安全抽象

一个常见的模式是使用 unsafe 代码创建安全的抽象。这意味着在内部使用 unsafe 代码,但对外提供安全的 API:

// 一个安全的抽象,内部使用 unsafe
pub struct SafeWrapper {data: *mut i32,len: usize,
}impl SafeWrapper {// 安全的公共 APIpub fn new(size: usize) -> Self {let data = unsafe {// 分配内存let layout = std::alloc::Layout::array::<i32>(size).unwrap();let ptr = std::alloc::alloc(layout) as *mut i32;// 初始化内存for i in 0..size {*ptr.add(i) = 0;}ptr};SafeWrapper { data, len: size }}pub fn get(&self, index: usize) -> Option<i32> {if index < self.len {unsafe {Some(*self.data.add(index))}} else {None}}pub fn set(&mut self, index: usize, value: i32) -> bool {if index < self.len {unsafe {*self.data.add(index) = value;}true} else {false}}
}impl Drop for SafeWrapper {fn drop(&mut self) {unsafe {let layout = std::alloc::Layout::array::<i32>(self.len).unwrap();std::alloc::dealloc(self.data as *mut u8, layout);}}
}

3. 访问或修改可变静态变量

静态变量在整个程序运行期间都存在,其内存位置是固定的。可变静态变量可能导致数据竞争,因此访问它们被认为是不安全的:

// 不可变静态变量是安全的
static HELLO_WORLD: &str = "Hello, world!";// 可变静态变量需要 unsafe 访问
static mut COUNTER: u32 = 0;fn add_to_counter(inc: u32) {unsafe {COUNTER += inc;}
}fn main() {// 读取不可变静态变量是安全的println!("{}", HELLO_WORLD);// 修改可变静态变量需要 unsafeadd_to_counter(3);unsafe {println!("COUNTER: {}", COUNTER);}
}
线程安全的替代方案

在多线程环境中,应该避免使用可变静态变量。替代方案包括:

  1. 使用原子类型
use std::sync::atomic::{AtomicU32, Ordering};static ATOMIC_COUNTER: AtomicU32 = AtomicU32::new(0);fn add_to_counter(inc: u32) {ATOMIC_COUNTER.fetch_add(inc, Ordering::SeqCst);
}fn main() {add_to_counter(3);println!("COUNTER: {}", ATOMIC_COUNTER.load(Ordering::SeqCst));
}
  1. 使用互斥锁
use std::sync::Mutex;
use lazy_static::lazy_static;lazy_static! {static ref MUTEX_COUNTER: Mutex<u32> = Mutex::new(0);
}fn add_to_counter(inc: u32) {let mut counter = MUTEX_COUNTER.lock().unwrap();*counter += inc;
}fn main() {add_to_counter(3);println!("COUNTER: {}", *MUTEX_COUNTER.lock().unwrap());
}

4. 实现 Unsafe Trait

unsafe trait 是那些实现者必须保证某些不变性的 trait,但编译器无法验证这些不变性。

// 声明一个 unsafe trait
unsafe trait Dangerous {fn dangerous_method(&self);
}// 实现 unsafe trait 需要 unsafe impl
struct SafeType;unsafe impl Dangerous for SafeType {fn dangerous_method(&self) {println!("实现了危险特性");}
}fn main() {let safe = SafeType;// 调用 unsafe trait 的方法是安全的safe.dangerous_method();
}
实际应用:Send 和 Sync

Rust 标准库中最著名的 unsafe traitSendSync

  • Send:表示类型可以安全地在线程间传递所有权
  • Sync:表示类型可以安全地在线程间共享引用
// 一个既不是 Send 也不是 Sync 的类型
use std::rc::Rc;
use std::cell::Cell;struct MyNonThreadSafe {data: Rc<Cell<i32>>,
}// 一个手动实现 Send 和 Sync 的类型
struct MyThreadSafe {data: i32,
}// 这是安全的,因为 MyThreadSafe 只包含 i32,它本身就是 Send 和 Sync
unsafe impl Send for MyThreadSafe {}
unsafe impl Sync for MyThreadSafe {}

5. 访问 Union 的字段

union 是一种特殊的数据类型,它允许在同一内存位置存储不同类型的值。由于编译器无法知道当前存储的是哪种类型,访问 union 字段被认为是不安全的:

#[repr(C)]
union MyUnion {i: u32,f: f32,
}fn main() {let u = MyUnion { i: 42 };// 访问 union 字段需要 unsafeunsafe {println!("整数值: {}", u.i);// 危险:解释内存为不同类型let f_val = u.f;println!("浮点值: {}", f_val);}
}
Union 的应用场景

union 主要用于以下场景:

  1. 与 C 代码交互:C 语言中的联合体在 Rust 中对应 union
  2. 类型转换:在不使用 transmute 的情况下进行类型转换
  3. 内存优化:在内存受限的环境中节省空间
// 使用 union 进行类型转换
#[repr(C)]
union FloatBits {f: f32,i: u32,
}fn float_to_bits(f: f32) -> u32 {unsafe { FloatBits { f }.i }
}fn bits_to_float(i: u32) -> f32 {unsafe { FloatBits { i }.f }
}fn main() {let f = 42.42f32;let bits = float_to_bits(f);let f2 = bits_to_float(bits);println!("f: {}, bits: {:x}, f2: {}", f, bits, f2);
}

Unsafe 代码的安全封装

编写 unsafe 代码的关键原则是将不安全代码限制在尽可能小的范围内,并提供安全的抽象。

安全抽象的原则

  1. 明确不变性:清楚地定义和记录你的不安全代码所依赖的假设和不变性
  2. 最小化 unsafe 块:使 unsafe 块尽可能小,只包含真正需要不安全操作的代码
  3. 彻底测试:对包含 unsafe 代码的部分进行全面测试,包括边界情况
  4. 清晰文档:详细记录 unsafe 代码的假设、前提条件和不变性

示例:安全封装的 Vec

以下是一个简化版的 Vec 实现,展示了如何安全地封装 unsafe 代码:

pub struct MyVec<T> {ptr: *mut T,len: usize,capacity: usize,
}impl<T> MyVec<T> {pub fn new() -> Self {Self {ptr: std::ptr::null_mut(),len: 0,capacity: 0,}}pub fn with_capacity(capacity: usize) -> Self {let layout = std::alloc::Layout::array::<T>(capacity).unwrap();let ptr = unsafe { std::alloc::alloc(layout) as *mut T };Self {ptr,len: 0,capacity,}}pub fn push(&mut self, value: T) {if self.len == self.capacity {self.grow();}unsafe {std::ptr::write(self.ptr.add(self.len), value);}self.len += 1;}pub fn pop(&mut self) -> Option<T> {if self.len == 0 {return None;}self.len -= 1;unsafe {Some(std::ptr::read(self.ptr.add(self.len)))}}pub fn get(&self, index: usize) -> Option<&T> {if index >= self.len {return None;}unsafe {Some(&*self.ptr.add(index))}}pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {if index >= self.len {return None;}unsafe {Some(&mut *self.ptr.add(index))}}fn grow(&mut self) {let new_capacity = if self.capacity == 0 { 1 } else { self.capacity * 2 };let new_layout = std::alloc::Layout::array::<T>(new_capacity).unwrap();let new_ptr = if self.capacity == 0 {unsafe { std::alloc::alloc(new_layout) as *mut T }} else {let old_layout = std::alloc::Layout::array::<T>(self.capacity).unwrap();unsafe {std::alloc::realloc(self.ptr as *mut u8,old_layout,new_layout.size(),) as *mut T}};self.ptr = new_ptr;self.capacity = new_capacity;}
}impl<T> Drop for MyVec<T> {fn drop(&mut self) {if self.capacity == 0 {return;}// 析构所有元素for i in 0..self.len {unsafe {std::ptr::drop_in_place(self.ptr.add(i));}}// 释放内存unsafe {let layout = std::alloc::Layout::array::<T>(self.capacity).unwrap();std::alloc::dealloc(self.ptr as *mut u8, layout);}}
}

常见的 Unsafe 模式

1. 内存映射

将文件或设备映射到内存中,直接访问:

use std::fs::File;
use memmap2::MmapOptions;fn main() -> std::io::Result<()> {let file = File::open("data.bin")?;let mmap = unsafe { MmapOptions::new().map(&file)? };// 直接访问内存映射的文件内容if mmap.len() >= 8 {let value = unsafe {// 将前 8 个字节解释为 u64let ptr = mmap.as_ptr() as *const u64;*ptr};println!("First 8 bytes as u64: {}", value);}Ok()
}

2. 类型转换

在不同类型之间进行转换,绕过类型系统:

fn transmute_example() {let array: [u8; 4] = [0x12, 0x34, 0x56, 0x78];// 使用 transmute 将字节数组转换为 u32let num: u32 = unsafe { std::mem::transmute(array) };println!("Transmuted value: 0x{:x}", num);// 更安全的替代方法let num2 = u32::from_ne_bytes(array);println!("Safe conversion: 0x{:x}", num2);
}

3. 内存对齐和填充

访问结构体的内部布局和填充:

use std::mem;#[repr(C)]
struct Aligned {a: u8,b: u32,c: u16,
}fn alignment_example() {let instance = Aligned { a: 1, b: 2, c: 3 };let ptr = &instance as *const Aligned as *const u8;println!("Size: {}, Alignment: {}", mem::size_of::<Aligned>(), mem::align_of::<Aligned>());// 访问内部字节,包括填充for i in 0..mem::size_of::<Aligned>() {let byte = unsafe { *ptr.add(i) };println!("Byte at offset {}: 0x{:02x}", i, byte);}
}

Unsafe 代码的常见陷阱

1. 未初始化内存

访问未初始化内存是未定义行为:

fn uninitialized_memory_trap() {// 危险:创建未初始化内存let mut data: [u8; 1000];// 错误:使用未初始化内存// unsafe { println!("{:?}", data); }// 正确:使用 MaybeUninituse std::mem::MaybeUninit;let mut data: [MaybeUninit<u8>; 1000] = unsafe {MaybeUninit::uninit().assume_init()};// 初始化部分数据for i in 0..data.len() {data[i] = MaybeUninit::new(42);}// 安全地转换为初始化数组let initialized_data: [u8; 1000] = unsafe {std::mem::transmute(data)};println!("First byte: {}", initialized_data[0]);
}

2. 悬垂指针

使用已释放的内存是未定义行为:

fn dangling_pointer_trap() {let ptr;{let value = 42;ptr = &value as *const i32;// value 在这里离开作用域并被释放}// 危险:使用悬垂指针// unsafe { println!("{}", *ptr); }// 正确:确保指针有效期内使用let value = 42;let ptr = &value as *const i32;unsafe { println!("{}", *ptr); }
}

3. 数据竞争

多线程同时访问可变数据是未定义行为:

use std::thread;fn data_race_trap() {static mut SHARED: i32 = 0;// 危险:多线程访问可变静态变量let handles: Vec<_> = (0..10).map(|_| {thread::spawn(|| {// 这会导致数据竞争for _ in 0..1000 {unsafe { SHARED += 1; }}})}).collect();for handle in handles {handle.join().unwrap();}// 结果可能不是 10000unsafe { println!("SHARED: {}", SHARED); }// 正确:使用原子操作use std::sync::atomic::{AtomicI32, Ordering};static ATOMIC_SHARED: AtomicI32 = AtomicI32::new(0);let handles: Vec<_> = (0..10).map(|_| {thread::spawn(|| {for _ in 0..1000 {ATOMIC_SHARED.fetch_add(1, Ordering::SeqCst);}})}).collect();for handle in handles {handle.join().unwrap();}// 结果总是 10000println!("ATOMIC_SHARED: {}", ATOMIC_SHARED.load(Ordering::SeqCst));
}

4. 类型混淆

将内存解释为错误的类型是未定义行为:

fn type_confusion_trap() {let value: f64 = 42.5;let ptr = &value as *const f64;// 危险:将 f64 解释为 i32// unsafe { println!("{}", *(ptr as *const i32)); }// 正确:使用适当的类型转换let bits = value.to_bits();println!("f64 as bits: 0x{:x}", bits);
}

调试 Unsafe 代码

工具和技术

  1. MIRI(MIR Interpreter):可以检测许多 unsafe 代码中的未定义行为
rustup +nightly component add miri
cargo +nightly miri test
  1. Address Sanitizer:检测内存错误
CRUSTFLAGS="-Z sanitizer=address" cargo test
  1. Valgrind:检测内存泄漏和访问错误
valgrind --leak-check=full ./target/debug/my_program

调试技巧

  1. 使用断言:在 unsafe 代码中使用断言验证假设
unsafe fn process_buffer(ptr: *mut u8, len: usize) {// 验证指针非空assert!(!ptr.is_null());// 验证长度合理assert!(len > 0 && len <= 1024);// 处理缓冲区...
}
  1. 记录不变性:清晰地记录 unsafe 代码的不变性和假设
// 安全不变性:
// 1. ptr 必须指向有效的、对齐的内存区域
// 2. 内存区域必须至少有 len 个字节
// 3. 内存区域在函数执行期间不能被其他代码修改
unsafe fn process_memory(ptr: *const u8, len: usize) {// 实现...
}

最佳实践

何时使用 Unsafe

  1. 必要时才使用:只有当安全 Rust 无法表达你的意图时才使用 unsafe
  2. 最小化范围:将 unsafe 块限制在尽可能小的范围内
  3. 安全封装:将 unsafe 代码封装在安全的抽象后面
  4. 彻底测试:对包含 unsafe 代码的部分进行全面测试

文档化 Unsafe 代码

unsafe 代码提供详细的文档,包括:

  1. 为什么需要 unsafe:解释为什么安全 Rust 不足以解决问题
  2. 安全不变性:记录代码依赖的所有假设和不变性
  3. 使用条件:明确使用该代码的前提条件
  4. 验证策略:描述如何验证代码的正确性
/// 将内存区域解释为指定类型的切片。
///
/// # Safety
///
/// 调用者必须确保:
/// 1. `ptr` 指向有效的、对齐的内存区域
/// 2. 内存区域至少包含 `len * size_of::<T>()` 字节
/// 3. 内存区域包含有效的 T 类型实例
/// 4. 返回的切片在使用期间,内存区域不会被其他代码修改
pub unsafe fn as_slice<T>(ptr: *const T, len: usize) -> &'static [T] {std::slice::from_raw_parts(ptr, len)
}

替代 Unsafe 的安全方法

在使用 unsafe 之前,考虑是否有安全的替代方法:

  1. 使用标准库:标准库已经提供了许多安全的抽象
  2. **使用第三方

相关文章:

Rust从入门到精通之精通篇:22.Unsafe Rust 详解

Unsafe Rust 详解 在 Rust 的设计哲学中&#xff0c;安全性是核心原则之一。Rust 的所有权系统、借用检查器和类型系统共同保证了内存安全和线程安全。然而&#xff0c;有些底层操作无法通过 Rust 的安全检查机制进行验证&#xff0c;这就是 unsafe Rust 存在的原因。在本章中…...

Three.js 快速入门教程【十八】射线拾取模型——鼠标点击屏幕选中模型或物体

系列文章目录 Three.js 快速入门教程【一】开启你的 3D Web 开发之旅 Three.js 快速入门教程【二】透视投影相机 Three.js 快速入门教程【三】渲染器 Three.js 快速入门教程【四】三维坐标系 Three.js 快速入门教程【五】动画渲染循环 Three.js 快速入门教程【六】相机控件 Or…...

如何下载 Postman?快速指南!

Postman 是一款非常受欢迎的 API 测试工具。它最初是作为一个 Chrome 插件发布&#xff0c;后来发展成为一款独立的跨平台软件&#xff0c;支持 Windows、Mac、Linux 等操作系统。 Postman 怎么下载教程&#xff08;2025最新版&#xff09;&#xff1f;...

Shiro学习(一):Shiro介绍和基本使用

一、Shiro介绍 1、百科对shiro的定义如下&#xff1a; Apache Shiro 一个强大且易于使用的 Java 安全框架&#xff0c;它提供了身份验证、授权、加密和会话管理等功能。Shiro 的设计目标是简化企业级应用程序的安全性开发过程&#xff0c;同时保持代码的简洁和易于维护。 2、…...

【git】基本操作

添加文件进本地仓库 git add 文件名删除文件 git rm 文件名版本回退 git reset [--sort| -- mixed | -- hard] sort选项: 只回退版本库&#xff0c;不回退暂存区和工作区 mixed&#xff08;reset的默认选项&#xff09;: 回退版本库和暂存区&#xff0c;不回退工作区 hard :…...

7.1 分治-快排专题:LeetCode 75. 颜色分类

1. 题目链接 LeetCode 75. 颜色分类 2. 题目描述 给定一个包含红色&#xff08;0&#xff09;、白色&#xff08;1&#xff09;和蓝色&#xff08;2&#xff09;的数组 nums&#xff0c;要求原地对数组进行排序&#xff0c;使得相同颜色的元素相邻&#xff0c;且按红、白、蓝…...

深度解析:TOML、XML、YAML及其他配置/数据格式对比

深度解析&#xff1a;TOML、XML、YAML及其他配置/数据格式对比 在软件开发和系统配置中&#xff0c;选择合适的配置或数据格式至关重要。本文将对比 TOML、XML、YAML 等常见格式&#xff0c;梳理它们的核心特性、适用场景及区别&#xff0c;并扩展介绍其他类似格式&#xff0c…...

开源软件许可证冲突的原因和解决方法

1、什么是开源许可证以及许可证冲突产生的问题 开源软件许可证是一种法律文件&#xff0c;它规定了软件用户、分发者和修改者使用、复制、修改和分发开源软件的权利和义务。开源许可证是由软件的版权所有者&#xff08;通常是开发者或开发团队&#xff09;发布的&#xff0c;它…...

详解java体系实用知识总结

0.java技术能力框架 基础模块应用模块综合模块技术岗位与面试流程常用工具集系统架构设计计算机基础常用框架微服务架构jvm原理缓存容器化多线程队列云计算&#xff08;阿里云/aws&#xff09;设计模式数据库数据结构与算法 1.常用设计模式与应用场景 工厂模式&#xff1a;s…...

node-ddk,electron,主进程通讯,窗口间通讯

node-ddk,electron,主进程通讯,窗口间通讯 https://blog.csdn.net/eli960/article/details/146207062 也可以下载demo直接演示 http://linuxmail.cn/go#node-ddk import 在主进程 import main, { NODEDDK } from "node-ddk/main"在渲染进程 import renderer, …...

kubectl 命令参数详解与示例

kubectl 命令参数详解与示例 kubectl 是 Kubernetes 的命令行工具&#xff0c;用于与 Kubernetes 集群交互。下面我将详细介绍 kubectl 的主要命令参数&#xff0c;并提供相应的使用示例。 一、基础命令 1. kubectl get - 获取资源信息 常用参数&#xff1a; -n, --namesp…...

在 Ubuntu 20.04 上重新启动网络

参考链接&#xff1a; 如何在 Ubuntu 22.04 上重新启动网络 执行以下两条命令&#xff0c;ok sudo nmcli networking off sudo nmcli networking on...

STM32 - 在机器人、自动化领域,LL库相比HAL优势明显

在机器人控制器、电机控制器等领域的开发&#xff0c;需要高实时性、精细化控制或者对代码执行效率、占用空间有较高要求。所以&#xff0c;大家常用的HAL库明显不符合要求。再加上&#xff0c;我们学习一门技术&#xff0c;一定要学会掌握底层的原理。MCU开发的底层就是寄存器…...

【区块链安全 | 第二篇】区块链概念详解

文章目录 概述1. 区块链类型2 区块链五层架构3 账本模型4. 节点&#xff08;Node&#xff09;5. 区块&#xff08;Block&#xff09;6. 区块链&#xff08;Blockchain&#xff09;7. 区块链工作流程 核心技术1. 共识机制2. 智能合约 主要组件1. 交易&#xff08;Transaction&am…...

【开源宝藏】30天学会CSS - DAY6 第六课 流光文字动画

第 0 步&#xff1a;项目结构 lighting-text/├─ index.html└─ style.cssindex.html&#xff1a;包含列表 <ul>&#xff0c;其中每个 <li> 放一个字母或符号。style.css&#xff1a;设置背景、文字样式&#xff0c;以及关键帧动画&#xff08;lighting&#xf…...

linux - centos7 部署 redis6.0.5

事先说明 本篇文章只解决在部署redis中出现的问题&#xff0c;并没有部署redis的全过程&#xff0c;详细部署过程可以参考Linux安装部署Redis(超级详细) - 长沙大鹏 - 博客园 执行 make 命令时报错 原因&#xff1a;是因为gcc版本太低 升级gcc版本时 出现没有可用软件包 devt…...

Java反射机制详解:原理、应用与最佳实践

Java反射机制详解&#xff1a;原理、应用与最佳实践 1. 什么是反射&#xff1f; Java反射&#xff08;Reflection&#xff09;是指在运行时动态获取类的信息&#xff08;如类名、方法、字段、构造方法等&#xff09;并操作对象的能力。它允许程序在运行时检查和修改类的行为&…...

Swift实现嵌套json字典重排序并输出string

在网络请求或接口签名中&#xff0c;通常要求将参数按照一定规则拼接成字符串。一个常见的做法是对字典的 key 进行排序&#xff0c;然后按照 “keyvalue” 的格式拼接&#xff0c;多个参数之间以特定符号&#xff08;例如 &&#xff09;连接。 如果参数中包含嵌套的字典或…...

【Ai】--- 可视化 DeepSeek-r1 接入 Open WebUI(超详细)

在编程的艺术世界里,代码和灵感需要寻找到最佳的交融点,才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里,我们将共同追寻这种完美结合,为未来的世界留下属于我们的独特印记。【Ai】--- 可视化 DeepSeek-r1 接入 Open WebUI(超详细) 开发环境一、前情提要:你…...

VSCode加Cline插件加DeepSeek实现AI编程指南

VSCode加Cline插件加DeepSeek实现AI编程指南 简介 本文将详细介绍如何在VSCode中使用Cline插件结合DeepSeek AI实现高效的AI辅助编程&#xff0c;特别适合初学者快速上手。我们将通过实现一个TodoList应用的例子来演示整个流程。 环境准备 1. 安装VSCode 前往VSCode官网下…...

代码规范之Variable Names变量名

代码规范之Variable Names变量名 golang中 官方文档&#xff1a;https://go.dev/wiki/CodeReviewComments#variable-names Variable names in Go should be short rather than long. This is especially true for local variables with limited scope. Prefer c to lineCoun…...

2025春招市场迎AI热潮:生成式人工智能(GAI)认证如何重构人才竞争力

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已逐渐渗透到我们生活的方方面面&#xff0c;从智能家居到自动驾驶&#xff0c;从智能客服到医疗诊断&#xff0c;AI的身影无处不在。而在这股AI浪潮中&#xff0c;生成式人工智能&#xff08;Generative AI,…...

Flink基础简介和安装部署

文章目录 一、Flink基础简介1、什么是Flink2、Flink流处理特性3、Flink四大基石4、Flink中的角色 二、Flink集群搭建1、Local模式①上传Flink安装包②启动交互窗口③提交任务测试④访问WebUI页面查看⑤退出停止集群 2、Standalone模式①修改配置⽂件 conf/flink-conf.yaml②修改…...

SpringBoot分布式项目实战:观察者模式的高阶应用与避坑指南

一、痛点场景&#xff1a;当观察者遇上分布式 在某电商平台重构项目中&#xff0c;我们遭遇了这样的困境&#xff1a;订单中心完成支付后需要触发库存扣减、积分结算、物流调度等12个后续操作。最初的实现采用了硬编码调用&#xff1a; // 伪代码示例 public void paySuccess…...

How to use pgbench to test performance for PostgreSQL?

pgbench 是一个用于测试 PostgreSQL 数据库性能的基准测试工具。通过模拟多个客户端并发执行 SQL 查询&#xff0c;它可以帮助你评估数据库的性能。以下是使用 pgbench 的基本步骤&#xff1a; 安装 pgbench pgbench 是 PostgreSQL 的一部分&#xff0c;因此在安装 PostgreSQ…...

C#基础学习(五)函数中的ref和out

1. 引言&#xff1a;为什么需要ref和out&#xff1f; ​问题背景&#xff1a;函数参数默认按值传递&#xff0c;值类型在函数内修改不影响外部变量&#xff1b;引用类型重新赋值时外部对象不变。​核心作用&#xff1a;允许函数内部修改外部变量的值&#xff0c;实现“双向传参…...

从零构建大语言模型全栈开发指南:第二部分:模型架构设计与实现-2.2.2文本生成逻辑:Top-k采样与温度控制

👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 2.2.2 文本生成逻辑:Top-k采样与温度控制1. 文本生成的核心挑战与数学框架1.1 自回归生成的基本流程2. `Top-k`采样原理与工程实现2.1 数学定义与算法流程2.2 PyTorch实现优化3. 温度控制的数学本质与参…...

关于CodeJava的学习笔记——9

一、IO流 1、定义 IInput输入 OOutput输出 流 : 数据从源点传输到汇点的"管道"而已 2、流的分类 按照方向分: 输入流 输出流 *:参照物当前Java程序为参照物 按照单位分: 字节流 字符流 按照功能分: 节点流 过滤流(包装流 处…...

LeetCode算法题(Go语言实现)_11

题目 给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相对位置形成的新字符串。&#xff08;例如&#xff0c;"ace"是"abcde"的一个子序列&a…...

Python----数据分析(足球运动员数据分析)

一、数据展示 1.1、数据 1.2、列名 字段名备注Name姓名Nationality国籍National_Position国家队位置National_Kit国家队号码Club所在俱乐部Club_Position所在俱乐部位置Club_Kit俱乐部号码Club_Joining加入俱乐部时间Contract_Expiry合同到期时间Rating评分Height身高Weight体…...