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

如何在Rust函数中返回含&str类型字段的Website结构体?

问题分析与解决:Rust中E0515返回引用临时值错误

问题背景

尝试实现从hosts文件解析网站信息的功能,核心代码如下:

工具函数与解析函数

// 意图去除冗余空白并按空白分割字符串
fn split_whitespace(string: &str) -> Vec<&str> {
    let words: Vec<&str> = string
        .split_whitespace()
        .filter(|&word| word != "")
        .collect();
    words
}

pub fn get_websites_from_hosts(hosts_path: &Path) -> Result<Vec<Website>, std::io::Error> {
    let hosts_string = read_to_string(hosts_path)?;

    let result: Vec<Website> = hosts_string
        .lines()
        .filter(|&line| split_whitespace(&line.to_owned()).len() == 2)
        .map(|line| {
            let ip_domain = split_whitespace(&line.to_owned());
            Website {
                domain_name: ip_domain[1], // 原以为引用hosts_string,但实际引用临时值
                redirect_ip: ip_domain[0], // 同上
                is_blocked: true,
                hosts_path,
            }
        })
        .collect();

    Ok(result)
}

Website结构体定义

#[derive(Debug, Clone)]
pub struct Website<'a> {
    pub domain_name: &'a str,
    pub redirect_ip: &'a str,
    pub is_blocked: bool,
    pub hosts_path: &'a Path,
}

编译错误信息

error[E0515]: cannot return value referencing temporary value
  --> src/parser.rs:21:13
   |
20 |               let ip_domain = split_whitespace(&line.to_owned());
   |                                                 --------------- temporary value created here
21 | /             Website {
22 | |                 domain_name: ip_domain[1],
23 | |                 redirect_ip: ip_domain[0],
24 | |                 is_blocked: true,
25 | |                 hosts_path,
26 | |             }
   | |_____________^ returns a value referencing data owned by the current function

For more information about this error, try `rustc --explain E0515`.
error: could not compile `website-blocker` due to previous error
warning: build failed, waiting for other jobs to finish...
error: could not compile `website-blocker` due to previous error

错误深层解释

  1. 临时值引用失效:代码中line.to_owned()会创建一个临时的String实例,split_whitespace返回的&str是对这个临时字符串的引用。但临时字符串在闭包执行结束后就会被销毁,导致Website中的domain_name和redirect_ip引用了已释放的内存,触发E0515错误。

  2. 隐藏的局部变量生命周期问题:即使去掉to_owned()直接使用line(hosts_string的切片),hosts_string是函数内部的局部变量——当函数返回时,hosts_string会被销毁,此时Website中的引用会变成悬空引用,编译器会抛出E0597错误(当前代码因临时值错误先触发了E0515,未暴露这个问题)。

额外说明:split_whitespace中的filter(|&word| word != "")是多余的,str::split_whitespace本身会跳过所有空白字符,不会返回空字符串。

修复方案(尽可能不修改Website结构体)

要解决问题,需确保被引用的数据生命周期与Website实例一致。这里通过返回一个封装了hosts内容所有权和网站列表的结构体来实现:

1. 定义结果封装结构体

use std::path::Path;

#[derive(Debug, Clone)]
pub struct Website<'a> {
    pub domain_name: &'a str,
    pub redirect_ip: &'a str,
    pub is_blocked: bool,
    pub hosts_path: &'a Path,
}

// 同时持有hosts内容的所有权和解析出的网站列表
pub struct HostsParseResult<'a> {
    pub hosts_content: String,
    pub websites: Vec<Website<'a>>,
}

2. 修改解析函数

use std::fs::read_to_string;
use std::path::Path;

fn split_whitespace(string: &str) -> Vec<&str> {
    // 简化函数,去除多余的filter
    string.split_whitespace().collect()
}

pub fn get_websites_from_hosts(hosts_path: &Path) -> Result<HostsParseResult, std::io::Error> {
    let hosts_string = read_to_string(hosts_path)?;

    let websites: Vec<Website> = hosts_string
        .lines()
        // 直接使用line(hosts_string的切片),无需创建临时字符串
        .filter(|line| split_whitespace(line).len() == 2)
        .map(|line| {
            let ip_domain = split_whitespace(line);
            Website {
                domain_name: ip_domain[1],
                redirect_ip: ip_domain[0],
                is_blocked: true,
                hosts_path,
            }
        })
        .collect();

    Ok(HostsParseResult {
        hosts_content: hosts_string,
        websites,
    })
}

此方案中,HostsParseResult的hosts_content持有字符串所有权,websites中的Website引用该字符串的切片,生命周期完全匹配,不会出现悬空引用问题。

通用处理技巧

  • 警惕临时值陷阱:避免在需要返回引用的场景中创建临时String(如to_owned()、clone()),尽量直接使用原始数据的切片,减少不必要的内存分配。
  • 明确生命周期绑定:若结构体包含引用,必须保证被引用数据的生命周期不短于结构体实例。如果函数内创建了被引用数据,要么将数据所有权与结构体一起返回,要么让调用者控制数据生命周期(如传入数据)。
  • 重视编译器错误提示:E0515、E0597等错误会明确指出引用的来源和数据所有者,仔细阅读提示可快速定位问题。
  • 优先选择所有权模式:如果允许修改结构体,将&str改为String可彻底规避引用生命周期问题——直接持有数据所有权比依赖引用更简单安全,尤其在返回数据的场景下。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 10:40:33