Rust Async 异步编程(六):Pin 和 Unpin

《Rust 程序设计语言》第十七章:异步编程基础:Async、Await、Future 与 Stream 本文介绍了Rust异步编程的核心概念,包括Future、Stream、async/await语法及其应用场景。主要内容包括: 基础概念:异步编程通过Future和Stream实现并发,async/await语法简化异步代码编写。Future表示未来可能就绪的值,Stream是异步版的迭代器。 关键工具: async标记可暂停的代码块 await等待异步操作完成 运行时(runtime)管理异步任务执行 join/race等组合器处理多个Future 典型应用: 网络请求并发处理 消息通道实现任务间通信 流式 阅读详情

Rust Async 异步编程(六):Pin 和 Unpin

在 Rust 异步编程中,有一个定海神针般的存在,它就是 Pin,作用说简单也简单,说复杂也非常复杂,当初刚出来时就连一些 Rust 大佬都一头雾水,何况瑟瑟发抖的我。好在今非昔比,目前网上的资料已经很全,而我就借花献佛,给大家好好讲讲这个 Pin。

什么是 Pin?

Pin 和 Unpin 标记一起工作。

Pin 会保证实现了 !Unpin 的对象永远不会被移动。

在 Rust 中,所有的类型可以分为两类:

  • 类型的值可以在内存中安全地被移动,例如数值、字符串、布尔值、结构体、枚举,总之你能想到的几乎所有类型都可以落入到此范畴内。
  • 自引用类型,大魔王来了,大家快跑,在之前章节我们已经见识过它的。

下面就是一个自引用类型:

struct SelfRef {
    value: String,
    pointer_to_value: *mut String,
}

在上面的结构体中,pointer_to_value 是一个原生指针,指向第一个字段 value 持有的字符串 String。很简单对吧?现在考虑一个情况,若 String 被移动了怎么办?

此时一个致命的问题就出现了:新的字符串的内存地址变了,而 pointer_to_value 依然指向之前的地址,一个重大bug就出现了!

灾难发生,英雄在哪?只见 Pin 闪亮登场,它可以防止一个类型在内存中被移动。再来回忆下之前在 Future 章节中,我们提到过在 poll 方法的签名中有一个 self: Pin<&mut Self>,那么为何要在这里使用 Pin 呢?

为何需要 Pin?

let fut_one = /* ... */; // Future 1
let fut_two = /* ... */; // Future 2
async move {
    fut_one.await;
    fut_two.await;
}

在底层,async 会创建一个实现了 Future trait 的匿名类型,并提供了一个 poll 方法:

// `async { ... }`语句块创建的 `Future` 类型
struct AsyncFuture {
    fut_one: FutOne,
    fut_two: FutTwo,
    state: State,
}

// `async` 语句块可能处于的状态
enum State {
    AwaitingFutOne,
    AwaitingFutTwo,
    Done,
}

impl Future for AsyncFuture {
    type Output = ();

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
        loop {
            match self.state {
                State::AwaitingFutOne => match self.fut_one.poll(..) {
                    Poll::Ready(()) => self.state = State::AwaitingFutTwo,
                    Poll::Pending => return Poll::Pending,
                }
                State::AwaitingFutTwo => match self.fut_two.poll(..) {
                    Poll::Ready(()) => self.state = State::Done,
                    Poll::Pending => return Poll::Pending,
                }
                State::Done => return Poll::Ready(()),
            }
        }
    }
}

当 poll 第一次被调用时,它会去查询 fut_one 的状态,若 fut_one 无法完成,则 poll 方法会返回。未来对 poll 的调用将从上一次调用结束的地方开始。该过程会一直持续,直到 Future 完成为止。

然而,如果我们的 async 语句块中使用了引用类型,会发生什么?例如下面例子:

async {
    let mut x = [0; 128];
    let read_into_buf_fut = read_into_buf(&mut x);
    read_into_buf_fut.await;
    println!("{:?}", x);
}

这段代码会编译成下面的形式:

struct ReadIntoBuf<'a> {
    buf: &'a mut [u8], // 指向下面的`x`字段
}

struct AsyncFuture {
    x: [u8; 128],
    read_into_buf_fut: ReadIntoBuf<'what_lifetime?>,
}

这里,ReadIntoBuf 拥有一个引用字段,指向了结构体的另一个字段 x,一旦 AsyncFuture 被移动,那 x 的地址也将随之变化,此时对 x 的引用就变成了不合法的,也就是 read_into_buf_fut.buf 会变为不合法的。

若能将 Future 在内存中固定到一个位置,就可以避免这种问题的发生,也就可以安全的创建上面这种引用类型。

Unpin

事实上,绝大多数类型都不在意是否被移动(开篇提到的第一种类型),因此它们都自动实现了 Unpin trait。

从名字推测,大家可能以为 Pin 和 Unpin 都是 trait 吧?实际上,Pin 不按套路出牌,它是一个结构体:

pub struct Pin<P> {
    pointer: P,
}

它包裹一个指针,并且能确保该指针指向的数据不会被移动,例如 Pin<&mut T>、Pin<&T>、Pin<Box<T>> ,都能确保 T 不会被移动。

在这里插入图片描述

而 Unpin 才是一个 trait,它表明一个类型可以随意被移动,那么问题来了,可以被 Pin 住的值,它有没有实现什么 trait 呢? 答案很出乎意料,可以被 Pin 住的值实现的 trait 是 !Unpin ,大家可能之前没有见过,但是它其实很简单,! 代表没有实现某个 trait 的意思,!Unpin 说明类型没有实现 Unpin trait,那自然就可以被 Pin 了。

那是不是意味着类型如果实现了 Unpin trait,就不能被 Pin 了?其实,还是可以 Pin 的,毕竟它只是一个结构体,你可以随意使用,但是不再有任何效果而已,该值一样可以被移动!

例如 Pin<&mut u8>,显然 u8 实现了 Unpin trait,它可以在内存中被移动,因此 Pin<&mut u8> 跟 &mut u8 实际上并无区别,一样可以被移动。

因此,一个类型如果不能被移动,它必须实现 !Unpin trait。如果类型拥有 !Unpin 标记,那么在 Pin 之后它们就无法移动了。

如果大家对 Pin、Unpin 还是模模糊糊,建议再重复看一遍之前的内容,理解它们对于我们后面要讲到的内容非常重要!

如果将 Unpin 与 Send/Sync 进行下对比,会发现它们都很像:

  • 都是 marker trait,该特征未定义任何行为,非常适用于标记
  • 都可以通过 ! 语法去除实现
  • 绝大多数情况都是自动实现,无需我们的操心

深入理解 Pin

对于上面的问题,我们可以简单的归结为如何在 Rust 中处理自引用类型。

下面用一个稍微简单点的例子来理解下 Pin:

#[derive(Debug)]
struct Test {
    a: String,
    b: *const String,
}

impl Test {
    fn new(txt: &str) -> Self {
        Test {
            a: String::from(txt),
            b: std::ptr::null(),
        }
    }

    fn init(&mut self) {
        let self_ref: *const String = &self.a;
        self.b = self_ref;
    }

    fn a(&self) -> &str {
        &self.a
    }

    fn b(&self) -> &String {
        assert!(!self.b.is_null(), "Test::b called without Test::init being called first");
        unsafe { &*(self.b) }
    }
}

Test 提供了方法用于获取字段 a 和 b 的值的引用。这里b 是 a 的一个引用,但是我们并没有使用引用类型而是用了原生指针,原因是:Rust 的借用规则不允许我们这样用,因为不符合生命周期的要求。 此时的 Test 就是一个自引用结构体。

如果不移动任何值,那么上面的例子将没有任何问题,例如:

fn main() {
    let mut test1 = Test::new("test1");
    test1.init();
    let mut test2 = Test::new("test2");
    test2.init();

    println!("a: {}, b: {}", test1.a(), test1.b());
    println!("a: {}, b: {}", test2.a(), test2.b());
}

输出非常正常:

a: test1, b: test1
a: test2, b: test2

我们就移动下数据试试,将 test 和 test2 进行下交换:

fn main() {
    let mut test1 = Test::new("test1");
    test1.init();
    let mut test2 = Test::new("test2");
    test2.init();

    println!("a: {}, b: {}", test1.a(), test1.b());
    std::mem::swap(&mut test1, &mut test2);
    println!("a: {}, b: {}", test2.a(), test2.b());
}

按理来说,这样修改后,输出应该如下:

a: test1, b: test1
a: test1, b: test1

但是实际运行后,却产生了下面的输出:

a: test1, b: test1
a: test1, b: test2

原因是 test2.b 指针依然指向了旧的地址,而该地址对应的值现在在 test1 里,最终会打印出意料之外的值。

下面的图片也可以帮助更好的理解这个过程:

在这里插入图片描述

Pin 在实践中的运用

在理解了 Pin 的作用后,我们再来看看它怎么帮我们解决问题。

将值固定到栈上

回到之前的例子,我们可以用 Pin 来解决指针指向的数据被移动的问题:

use std::pin::Pin;
use std::marker::PhantomPinned;

#[derive(Debug)]
struct Test {
    a: String,
    b: *const String,
    _marker: PhantomPinned,
}


impl Test {
    fn new(txt: &str) -> Self {
        Test {
            a: String::from(txt),
            b: std::ptr::null(),
            _marker: PhantomPinned, // 这个标记可以让我们的类型自动实现特征`!Unpin`
        }
    }

    fn init(self: Pin<&mut Self>) {
        let self_ptr: *const String = &self.a;
        let this = unsafe { self.get_unchecked_mut() };
        this.b = self_ptr;
    }

    fn a(self: Pin<&Self>) -> &str {
        &self.get_ref().a
    }

    fn b(self: Pin<&Self>) -> &String {
        assert!(!self.b.is_null(), "Test::b called without Test::init being called first");
        unsafe { &*(self.b) }
    }
}

上面代码中,我们使用了一个标记类型 PhantomPinned 将自定义结构体 Test 变成了 !Unpin(编译器会自动帮我们实现),因此该结构体无法再被移动。

一旦类型实现了 !Unpin ,那将它的值固定到栈上就是不安全的行为,因此在代码中我们使用了 unsafe 语句块来进行处理,你也可以使用 pin_utils 来避免 unsafe 的使用。

Rust 中的 unsafe 其实没有那么可怕,虽然听上去很不安全,但是实际上 Rust 依然提供了很多机制来帮我们提升了安全性,因此不必像对待 Go 语言的 unsafe 那样去畏惧于使用 Rust 中的 unsafe ,大致使用原则总结如下:没必要用时,就不要用,当有必要用时,就大胆用,但是尽量控制好边界,让 unsafe 的范围尽可能小。

此时,再去尝试移动被固定的值,就会导致编译错误:

pub fn main() {
    // 此时的`test1`可以被安全的移动
    let mut test1 = Test::new("test1");
    // 新的`test1`由于使用了`Pin`,因此无法再被移动,这里的声明会将之前的`test1`遮蔽掉(shadow)
    let mut test1 = unsafe { Pin::new_unchecked(&mut test1) };
    Test::init(test1.as_mut());

    let mut test2 = Test::new("test2");
    let mut test2 = unsafe { Pin::new_unchecked(&mut test2) };
    Test::init(test2.as_mut());

    println!("a: {}, b: {}", Test::a(test1.as_ref()), Test::b(test1.as_ref()));
    std::mem::swap(test1.get_mut(), test2.get_mut());
    println!("a: {}, b: {}", Test::a(test2.as_ref()), Test::b(test2.as_ref()));
}

注意,Rust 并不是在运行时做这件事,而是在编译期就完成了,因此没有额外的性能开销!来看看报错:

error[E0277]: `PhantomPinned` cannot be unpinned
   --> src/main.rs:47:43
    |
47  |     std::mem::swap(test1.get_mut(), test2.get_mut());
    |                                           ^^^^^^^ within `Test`, the trait `Unpin` is not implemented for `PhantomPinned`

需要注意的是固定在栈上非常依赖于你写出的 unsafe 代码的正确性。我们知道 &'a mut T 可以固定的生命周期是 'a ,但是我们却不知道当生命周期 'a 结束后,该指针指向的数据是否会被移走。如果你的 unsafe 代码里这么实现了,那么就会违背 Pin 应该具有的作用!

一个常见的错误就是忘记去遮蔽(shadow)初始的变量,因为你可以 drop 掉 Pin ,然后在 &'a mut T 结束后去移动数据:

fn main() {
   let mut test1 = Test::new("test1");
   let mut test1_pin = unsafe { Pin::new_unchecked(&mut test1) };
   Test::init(test1_pin.as_mut());

   drop(test1_pin);
   println!(r#"test1.b points to "test1": {:?}..."#, test1.b);

   let mut test2 = Test::new("test2");
   mem::swap(&mut test1, &mut test2);
   println!("... and now it points nowhere: {:?}", test1.b);
}

将值固定到堆上

将一个 !Unpin 类型的值固定到堆上,会给予该值一个稳定的内存地址,它指向的堆中的值在 Pin 后是无法被移动的。而且与固定在栈上不同,我们知道堆上的值在整个生命周期内都会被稳稳地固定住。

use std::pin::Pin;
use std::marker::PhantomPinned;

#[derive(Debug)]
struct Test {
    a: String,
    b: *const String,
    _marker: PhantomPinned,
}

impl Test {
    fn new(txt: &str) -> Pin<Box<Self>> {
        let t = Test {
            a: String::from(txt),
            b: std::ptr::null(),
            _marker: PhantomPinned,
        };
        let mut boxed = Box::pin(t);
        let self_ptr: *const String = &boxed.as_ref().a;
        unsafe { boxed.as_mut().get_unchecked_mut().b = self_ptr };

        boxed
    }

    fn a(self: Pin<&Self>) -> &str {
        &self.get_ref().a
    }

    fn b(self: Pin<&Self>) -> &String {
        unsafe { &*(self.b) }
    }
}

pub fn main() {
    let test1 = Test::new("test1");
    let test2 = Test::new("test2");

    println!("a: {}, b: {}",test1.as_ref().a(), test1.as_ref().b());
    println!("a: {}, b: {}",test2.as_ref().a(), test2.as_ref().b());
}

程序输出:

a: test1, b: test1
a: test2, b: test2

将固定住的 Future 变为 Unpin

async 函数返回的 Future 默认就是 !Unpin 的。

但是,在实际应用中,一些函数会要求它们处理的 Future 是 Unpin 的,此时,若你使用的 Future 是 !Unpin 的,必须要使用以下的方法先将 Future 进行固定:

  • Box::pin,创建一个 Pin<Box<T>>
  • pin_utils::pin_mut!,创建一个 Pin<&mut T>

固定后获得的 Pin<Box<T>> 和 Pin<&mut T> 既可以用于 Future,又会自动实现 Unpin。

// `pin_utils` 可以在crates.io中找到
use pin_utils::pin_mut;

// 函数的参数是一个`Future`,但是要求该`Future`实现`Unpin`
fn execute_unpin_future(x: impl Future<Output = ()> + Unpin) { /* ... */ }

let fut = async { /* ... */ };
// 下面代码报错: 
// 默认情况下,`fut` 实现的是`!Unpin`,并没有实现`Unpin`
// execute_unpin_future(fut); 

// 使用`Box`进行固定
let fut = async { /* ... */ };
let fut = Box::pin(fut);
execute_unpin_future(fut); // OK

// 使用`pin_mut!`进行固定
let fut = async { /* ... */ };
pin_mut!(fut);
execute_unpin_future(fut); // OK

总结

  1. 若 T: Unpin(Rust 类型的默认实现),那么 Pin<'a, T> 跟 &'a mut T 完全相同,也就是 Pin 将没有任何效果,该移动还是照常移动。
  2. 若 T: !Unpin,那么把 &mut T 变成 Pin 的 T,需要 unsafe 操作。
  3. 绝大多数标准库类型都实现了 Unpin,事实上,对于 Rust 中你能遇到的绝大多数类型,该结论依然成立,其中一个例外就是:async/await 生成的 Future 没有实现 Unpin。
  4. 你可以通过以下方法为自己的类型添加 !Unpin 约束:
    • 添加文中提到的 std::marker::PhantomPinned 到类型上
    • 使用 nightly 版本下的 feature flag
  5. 可以将值固定到栈上,也可以固定到堆上
    • 将 !Unpin 值固定到栈上需要使用 unsafe
    • 将 !Unpin 值固定到堆上无需 unsafe ,可以通过 Box::pin 来简单的实现
  6. 当固定类型 T: !Unpin 时,你需要保证数据从被固定到被 drop 这段时期内,其内存不会变得非法或者被重用

参考

  1. https://github.com/rustcn-org/async-book
  2. https://www.bilibili.com/video/BV1Ki4y1C7gj
Rust pin unpin理解 为什么说没有pin的数据移动是不安全的深入理解 看了网上一片文章,有了一些理解。想要理解pin与unpin主要要理解数据再移动的时候的不安全。就知道了pin的重要性。 阅读详情

相关推荐

使用 Wireshark 进行计算机网络协议分析

文章目录实验前准备一、Wireshark抓包示例二、Wireshark实作1、数据链路层实作一 熟悉Ethernet帧结构实作二 了解子网内/外通信时的 MAC 地址实作三 掌握 ARP 解析过程2、网络层实作一 熟悉 IP 包结构实作二 IP 包的分段与重组实作三 考察 TTL 事件3、传输层实作一 熟悉 TCP 和 UDP 段结构实作二 分析 TCP 建立和释放连接4、应用层实作一 了解 DNS 解析实作二 了解 HTTP 的请求和应答 实验前准备 本部分实验用到的是抓包和协议分析软件——Wiresha

weixin_51087836的博客 8201

Rust中的Pin详解

相关概念Pin<P<T>>这是一个struct,作用就是将P所指向的T在内存中固定住,不能移动。说白一些,就是不能通过safe代码拿到&mut T。Pin...

Rust语言学习交流 6270

FPGA 高速接口Aurora8B/10B 协议详解与仿真

Aurora 8B/10B 是一种用于高速串行通信的协议,通常用于 FPGA 设计和其他数字通信应用。即一种编码方案,旨在在传输数据时提供可靠性、时钟恢复和错误检测。主要用于在点对点串行链路间移动数据的可扩展轻量级链路层协议,为板对板、芯片对芯片以及一些单向链接提供低成本低资源的解决方案。信号源信号描述ref_clk时钟源GT的外部时钟INIT CLK时钟源全局复位,低电平有效DRP CLK时钟源DRP时钟,动态重配置时钟时钟源用户传输的数据钟及逻辑时钟域。

toonyhe的博客 4000

读懂 Pin,一次搞清 Rust 最难的指针

本文介绍了Rust中的Pin指针及其相关概念。Pin是一种智能指针包装器,用于固定内存地址,防止特定类型被移动。Unpin是标记trait,表示类型可以安全移动,而!Unpin类型需要Pin保护。Pin主要作用于栈上对象地址,确保内部引用安全。文章通过示例展示了Pin的API使用,包括Pin<Box<T>>、自引用结构和Future中的应用。Pin对!Unpin类型才真正有效,而Copy或Unpin类型即使被Pin也能移动。Pin在异步编程中保护自引用结构不被移动,确保Future执

taishanduba的专栏 986

深入解析Xilinx IDELAYE2在FPGA时序优化中的关键作用与实战配置

本文深入解析了Xilinx FPGA中IDELAYE2模块在时序优化中的关键作用。作为精密的信号延时微调器,IDELAYE2通过可编程的抽头延迟,有效解决高速接口中数据与时钟的相位偏差问题。文章详细阐述了其必须与IDELAYCTRL配对使用的核心机制,并对比分析了FIXED、VARIABLE和VAR_LOAD三种工作模式的实战配置方法与代码示例,为FPGA工程师提供了解决时序收敛难题的关键硬件配置指南。

pear55的博客 1184

YOLO26涨点改进 | 独家创新,注意力改进篇 | AAAI 2025 | YOLO26引入 DRM 防御优化模块,进行特征优化/特征增强,助力目标检测、图像分类、图像分割有效涨点

本文提出了一种基于DRM防御优化模块改进的YOLO26网络模型,显著提升了目标检测的鲁棒性和特征提取能力。DRM模块通过优化特征提取过程、增强抗噪声能力和补充细节信息,有效提高了模型在复杂场景下的检测精度和定位准确度。该模块可集成于UNet等主干网络,采用Transformer结构实现特征精炼,在对抗攻击环境下仍能保持稳定的融合效果。实验表明,改进后的模型在红外与可见光图像融合任务中表现出色,同时提升了下游任务性能。文章详细介绍了DRM模块的结构、作用机制及实现代码,并提供了两种YOLO26改进方案.

Ai缝合怪、助力小伙伴高效发论文 145

对Rust异步编程中Pin(固定)的理解

作为有栈协程,最终Rust还是通过Pin曲线救国,在异步编程中也实现了其内存安全的承诺。

qq_50764711的博客 1429

17. ADC开发

bes2700 支持2路ADC。

雪飞_海 342

rust高级 异步编程 二 pin

脑袋里已经快被 Pin 、 Unpin 、!若 T: Unpin ( Rust 类型的默认实现),那么 Pin<'a, T> 跟 &'a mut T 完全相同,也就是 Pin 将没有任何效果, 该移动还是照常移动绝大多数标准库类型都实现了 Unpin ,事实上,对于 Rust 中你能遇到的绝大多数类型,该结论依然成立 ,其中一个例外就是:async/await 生成的 Future 没有实现 Unpin你可以通过以下方法为自己的类型添加!

个人博客,记录了本人学习的各种有趣的技术。 3178

【问题解决】解决 Python `PermissionError` 权限不足:文件/进程/端口全场景修复

本文总结了Python程序中常见的权限问题及解决方案,包括文件、进程和端口权限错误。文件权限问题通常由文件占用、权限不足或路径错误引起;进程问题多因权限不足或系统限制;端口问题则与端口占用、防火墙或特权端口相关。解决方案提供了详细的代码实现:文件权限管理器类(FilePermissionManager)包含检查权限(check_permission)、修复权限(fix_permission)、安全写入(safe_write)和安全删除(safe_delete)等方法,通过异常处理和临时文件等技术确保操作安全

博客 359

AutoSAR配置与实践(深入篇)5.4 OS原理(下 )- 内存保护(MPU)

ECU可能由内部模块、第三方模块等各模块集成的。需求对每个模块所定义的安全等级可能有差异,需要考虑不同安全等级的模块和代码间减少相互干扰,防止程序错误传播。可以理解为包含Task、Alarm、Counter、Schedule table、ISRs、Event等基本对象的容器,用于隔离Task/Alarm等,进行内存保护。

nihaoljq2010的博客 3538

Pin与Unpin:Rust异步编程的内存安全基石

Rust异步编程中的Pin和Unpin解析 本文深入探讨Rust异步编程中的核心概念Pin和Unpin。文章首先展示了自引用结构体在移动时产生的悬垂指针问题,解释了为什么异步编程特别需要解决这个问题。然后详细介绍了Pin的设计哲学,它通过类型系统确保被Pin的值不能移动,并通过Unpin trait区分哪些类型可以安全移动。 文章提供了Pin的基本API用法,对比了Unpin和!Unpin类型的差异,并通过一个手动实现的自引用Future示例,展示了如何安全构建自引用结构。最后还实现了一个完整的异步Time

Lexxn1的博客 1477

YOLOv11改进 - 卷积Conv | PATConv(Partial Attention Convolution)部分注意力卷积,在减少计算量的同时融合卷积与注意力的优势 | AAAI 2026

本文提出部分注意力卷积(PATConv)机制,并将其集成到YOLOv11中。传统神经网络中,卷积计算密集,注意力机制全局计算冗余,此前的“部分卷积”会丢失未计算通道的特征价值。PATConv通过“通道拆分 - 并行处理 - 结果拼接”的逻辑,给不同通道分配“擅长的任务”,兼顾局部与全局特征。基于此,它还衍生出PAT_ch、PAT_sp和PAT_sf三种细分模块。此外,还提出了动态部分卷积(DPConv),并构建了新的混合网络家族PartialNet。我们将PATConv代码集成到YOLOv11中,实验表明,

魔改工程师的博客 1085

Rust 中 Pin 与 Unpin 的内存安全保证以及 async/await 语法的展开原理

Rust 以其强大的内存安全性和高效的性能在系统编程领域崭露头角。异步编程在现代软件开发中变得越来越重要,Rust 提供了 async/await 这样的语法糖来简化异步代码的编写。而 Pin 和 Unpin 则是保障异步编程中内存安全的关键概念。理解它们对于编写正确、高效的 Rust 异步程序至关重要。

2401_84813926的博客 966

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Pin 与 Unpin:自引用结构体为什么不能移动

Pin和Unpin是 Rust 为自引用结构体提供的类型级安全保证。Pin的核心承诺是阻止!Unpin类型的值被移动,保证内部自引用有效。Unpin是默认行为,Pin对其无约束效果。实现要点:用标记使类型成为!Unpin、先 Pin 后建立自引用、Pin 传播确保遍历引用同样受约束。局限在于 unsafe 逃逸口的信任链风险、栈上 Pin 的脆弱性、以及 Unpin 隐式自动实现可能导致的标记遗漏。工程实践中,优先用Box::pin在堆上创建 Pin,严格审查所有调用点,确保自引用结构体的标记不被遗漏。

2301_81410839的博客 4172

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Rust By Practice 异步编程实战:从 async/await 到 Future、Pin 与 Stream 的完整路径

Rust By Practice 是一本面向 Rust 新手与进阶开发者的实战练习书,其异步编程章节系统覆盖了 async/await 语法、Future 机制、Pin 与 Unpin 以及 Stream 流处理四大核心主题。本书采用"示例 + 大量练习 + 实战项目"的练习驱动模式,帮助你从零开始真正掌握 Rust 异步编程,而不仅仅是停留在概念层面。 [![Rust By Practice

gitblog_00641的博客 407

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Rust Pin与Unpin详解:从自引用结构到异步状态机的内存固定机制

Pin/Unpin是Rust类型系统中为自引用结构提供安全保证的核心机制。Pin通过类型系统约束阻止被Pin的值被移动,Unpin则标记了移动安全的类型,豁免了Pin的约束。异步状态机是Pin最重要的应用场景——async函数编译为包含跨await点引用的状态机,Pin保证了状态机在await期间不会被移动。落地路线上,建议优先使用pin-project或pin-project-lite宏处理Pin投影,避免手写Unsafe投影代码;

2301_81410839的博客 3927

Pin 与 Unpin:Rust 的内存安全保证

在深入讨论 Pin 之前,我们需要理解一个根本性的问题:为什么 Rust 的所有权系统和借用检查器还不够?为什么还需要引入 Pin 这个复杂的概念?答案在于自引用结构体(self-referential struct)的存在。考虑一个经典场景:某个结构体内部包含一个字段,它保存的是指向该结构体其他字段的指针或引用。当这个结构体在内存中被移动时,指针变得无效——这是一个隐蔽但致命的内存安全问题。传统的 Rust 所有权系统无法追踪指针指向的是哪个字段,因此编译器无法阻止这种危险的移动。Pin 的核心职责就是冻

qq_51835683的博客 1181

Rust 异步 Pin 与 Unpin 完全指南:从自引用状态机到安全固定实战

> 本文是 RustTraining 仓库中 `async-book` 训练营([async-book/src/SUMMARY.md](https://link.gitcode.com/i/bf32dbff5fe8e666be13c2dd9de8a997))第 4 章的核心内容展开。异步 Rust 中最令人困惑的概念莫过于 `Pin` 与 `Unpin`:编译器会把 `async fn` 降级为自

gitblog_00297的博客 240

异步编程革命:Rust async/await完全指南

异步编程革命:Rust async/await完全指南 本文深入探讨了Rust异步编程的核心机制,从Future执行模型与任务调度机制、Pin和Unpin的内存安全原理,到Stream流处理与多Future并发技术,最后通过异步Web服务器实战开发展示完整应用。文章详细解析了Rust异步编程的底层实现原理和高级应用技巧,为开发者提供从基础到实战的完整指南。 Future执行模型与任务调度机制 在R...

gitblog_00027的博客 1262

Rust Pin 解析:核心原理与异步编程实践

在学习 Rust 的时候,Pin 绝对是最容易让人困惑的概念之一。它不像所有权、借用那样贯穿日常编码,却在异步编程、自引用结构等场景中扮演着重要的角色。很多开发者在接触 Pin 时,都会被不可移动、Unpin、不安全构造等概念绕晕,本文将从实际问题出发,层层拆解 Pin 的本质、用法与底层逻辑。

github_28443763的博客 565

Pin与Unpin的内存安全保证:驯服Rust中的自引用结构

理解Pin的工作原理、Unpin的语义以及它们如何共同保障内存安全,是掌握Rust异步编程和高级内存管理的关键。

超哥的博客 703

Rust Pin深度解析:从自引用陷阱到async执行器的底层设计

Rust的所有权与借用检查机制保证了内存安全,但在自引用数据结构与异步编程场景中,值的移动可能导致内部指针悬垂,形成未定义行为。Pin作为Rust标准库中的关键类型,通过类型系统巧妙限制可变访问,配合Unpin auto trait与PhantomPinned标记,从根本上阻止了非安全类型的移动。理解Pin的原理,是掌握Future状态机、编写async执行器及底层并发框架的基础。本文从自引用结构体的移动陷阱出发,逐步分析Pin的字段设计、Unpin的自动推导机制、条件化DerefMut实现,并对比Box:

weixin_30325487的博客 415

手写简化版Pin:深入理解Rust中禁止移动的核心机制

内存安全是系统编程的基石,而异步编程则对数据生命周期提出了更严苛的挑战。在Rust中,自引用结构体是异步状态机的常见形态,其内部指针会因内存移动而失效,从而引发悬垂指针风险。为解决这一问题,Rust引入了Pin机制,通过类型系统与借用检查器共同保证被钉住的值在安全代码中不可被移动。本文从自引用结构体的隐患出发,手写一个简化版Pin,逐步剖析Unpin、PhantomPinned等关键概念,并对照标准库说明设计边界。理解Pin不仅有助于掌握async/await底层原理,更是编写可靠异步代码、排查Future

weixin_33901641的博客 994

Rust异步编程实战:构建高性能网络应用

本文介绍了Rust异步编程的核心概念与实践方法。首先对比了同步与异步编程的区别,阐述了异步编程在高并发场景下的优势。随后详细讲解了Rust异步编程的演进历程,重点介绍了Future、Poll和Waker三个核心概念。文章通过代码示例展示了async/await语法的使用方式,包括异步函数的定义和执行流程。最后深入讲解了Tokio异步运行时的架构、安装配置方法,并提供了TCP服务器/客户端的实现示例,帮助开发者掌握Rust高性能网络应用的构建方法。

编程世界如山海,探索不止。 9679

Pin/Unpin 在异步 trait 中的实战困境:Async fn 在 trait 中的类型擦除与解决方案

// 自定义的自引用 Future////// 为什么需要 PhantomPinned:/// 这个 Future 包含内部借用——buf 引用指向 buffer 的切片/// move 该 Future 将破坏这个借用关系/// PhantomPinned 阻止自动实现 Unpin,确保 Pin 约束生效/// PhantomPinned 阻止编译器自动推导 Unpin/// 这确保了该 Future 被 Pin 包裹后才能 poll。

2301_81410839的博客 3379
上一篇: Rust Async 异步编程(五):async/.await
下一篇: Rust Async 异步编程(七):Stream 流处理
UestcXiye
博客等级 码龄8年 9031粉丝 1306原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值