You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

这段不安全Rust代码在Windows正常,Ubuntu运行出错的原因是什么?

问题分析与解决方案

你的代码在Linux上出现野指针问题,核心原因是引用生命周期绑定错误导致裸指针有效性未被保证,Windows上能运行只是编译器优化策略不同的侥幸结果。

具体问题点

你在new函数中通过let inner_ref = &**inner;创建的&str引用,其生命周期绑定的是栈上的临时变量inner(Pin<Box<String>>类型)。当你把inner移动到Container结构体中返回时,栈上的临时inner变量会被销毁,对应的引用生命周期也随之结束。

虽然堆上的String缓冲区还存在(因为inner的所有权转移到了Container),但基于过期引用转换的*const str裸指针,其有效性不再受Rust内存模型保障。Linux下的编译器会更严格地执行生命周期相关优化,导致该指针指向的内存被错误覆盖,出现乱码;而Windows下的编译器未触发该优化,代码侥幸运行。

修复方案

有两种可靠的修复方式,核心都是确保引用的生命周期与Container中的inner成员绑定:

方案1:修正引用来源,绑定正确生命周期

直接从inner成员获取&str,确保引用生命周期与inner绑定:

pub struct Container {
    inner: Pin<Box<String>>,
    half_a: *const str,
    half_b: *const str,
}

impl Container {
    const SEPARATOR: char = '-';

    pub fn new(input: impl AsRef<str>) -> Option<Self> {
        let input = input.as_ref();
        if input.is_empty() {
            return None;
        }

        let inner = Box::pin(input.to_string());
        // 直接从inner获取绑定其生命周期的&str
        let inner_str = inner.as_ref().as_str();

        let separator_index = inner_str.find(Container::SEPARATOR)?;
        let half_a = &inner_str[0..separator_index];
        let half_b = &inner_str[separator_index+1..];

        if half_a.is_empty() || half_b.is_empty() || half_b.contains(Container::SEPARATOR) {
            return None;
        }

        Some(Self {
            half_a: half_a as *const str,
            half_b: half_b as *const str,
            inner,
        })
    }
    
    pub fn get_half_a(&self) -> &str {
        unsafe {
            &*self.half_a
        }
    }

    pub fn get_half_b(&self) -> &str {
        unsafe {
            &*self.half_b
        }
    }
}

方案2:显式存储指针与长度(更推荐)

避免使用*const str胖指针,直接存储缓冲区指针和长度,手动构造&str,逻辑更清晰:

pub struct Container {
    inner: Pin<Box<String>>,
    half_a_ptr: *const u8,
    half_a_len: usize,
    half_b_ptr: *const u8,
    half_b_len: usize,
}

impl Container {
    const SEPARATOR: char = '-';

    pub fn new(input: impl AsRef<str>) -> Option<Self> {
        let input = input.as_ref();
        if input.is_empty() {
            return None;
        }

        let inner = Box::pin(input.to_string());
        let inner_str = inner.as_ref().as_str();

        let separator_index = inner_str.find(Container::SEPARATOR)?;
        let half_a = &inner_str[0..separator_index];
        let half_b = &inner_str[separator_index+1..];

        if half_a.is_empty() || half_b.is_empty() || half_b.contains(Container::SEPARATOR) {
            return None;
        }

        Some(Self {
            inner,
            half_a_ptr: half_a.as_ptr(),
            half_a_len: half_a.len(),
            half_b_ptr: half_b.as_ptr(),
            half_b_len: half_b.len(),
        })
    }
    
    pub fn get_half_a(&self) -> &str {
        unsafe {
            std::str::from_raw_parts(self.half_a_ptr, self.half_a_len)
        }
    }

    pub fn get_half_b(&self) -> &str {
        unsafe {
            std::str::from_raw_parts(self.half_b_ptr, self.half_b_len)
        }
    }
}

额外说明

你使用Pin<Box<String>>其实是多余的——Box<String>本身已经将String存储在堆上,移动Box不会改变堆上缓冲区的地址,Pin在这里无法提供额外的安全性。如果是为了学习自引用结构体,后续可以尝试更典型的自引用场景(比如结构体内部引用自身的栈成员)。

内容的提问来源于stack exchange,提问作者444RS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 13:37:03