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

解决Rust中‘borrowed value does not live long enough’错误的更优方案

解决Rust中"borrowed value does not live long enough"的更优方案

我在参与Advent of Code活动时编写Rust代码,遇到了borrowed value does not live long enough编译错误。目前用硬编码匹配颜色的临时方案解决,但这种方式在颜色数量增多时会非常繁琐,想寻求更优的解决办法。

原始代码

use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

fn main() {
    let mut coloured_cubes  = HashMap::from([
        ("red",   0),
        ("green", 0),
        ("blue",  0),
    ]);
    let mut game_nr         = 0;
    let mut sum_of_powers   = 0;

    let file                = std::env::args().nth(1).
        expect("Please specify a filename");

    if let Ok(lines) = read_lines(file) {
        for line in lines {
            if let Ok(mut cal_line) = line {
                game_nr += 1;
                coloured_cubes.insert("red",   0);
                coloured_cubes.insert("green", 0);
                coloured_cubes.insert("blue",  0);
                let prefix  = format!("Game {game_nr}: ");
                if ! cal_line.starts_with(&prefix) {
                    panic!("Line {game_nr} had a wrong prefix");
                }
                cal_line.replace_range(..prefix.len(), "");
                for game_part in cal_line.split("; ") {
                    for cube in game_part.split(", ") {
                        let cube_info: Vec<&str> = cube.split(' ').collect();
                        if cube_info.len() != 2 {
                            panic!("{prefix} wrong format '{cube}'");
                        }
                        let count  = cube_info[0].parse::<i32>().unwrap();
                        let colour = cube_info[1];
                        if count < 1 {
                            panic!("{} count should at least be 1 {:?}",
                                prefix, cube_info);
                        }
                        if ! coloured_cubes.contains_key(colour) {
                            panic!("{prefix} illegal colour {colour}");
                        }
                        if count > coloured_cubes[colour] {
                            let temp_colour = match colour {
                                "red"   => "red",
                                "green" => "green",
                                "blue"  => "blue",
                                _       => panic!("Got a wrong colour: {colour}")
                            };
                            coloured_cubes.insert(temp_colour, count);
                        }
                    }
                }
                sum_of_powers += coloured_cubes["red"] *
                    coloured_cubes["green"] * coloured_cubes["blue"];
            } else {
                panic!("Problem with line");
            }
        }
    } else {
        panic!("Did not get any lines!");
    }
    println!("ResultSum of powers: {sum_of_powers}");
}

// Functions
// The output is wrapped in a Result to allow matching on errors
// Returns an Iterator to the Reader of the lines of the file.
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

当前临时方案的问题

用temp_colour变量通过match硬编码匹配颜色字符串,虽然能绕过生命周期错误,但颜色数量增多时,需要不断扩展match分支,维护成本极高,也不符合Rust的类型安全理念。

错误根源

coloured_cubes初始使用的是字符串字面量(&'static str类型,生命周期为程序整个运行期),而从cal_line.split生成的colour是指向栈上字符串的引用,生命周期仅到当前循环迭代结束(cal_line被drop时)。直接将该引用插入HashMap,会导致HashMap持有一个已失效的引用,违反Rust的内存安全规则。

更优解决方案

有两种更优雅的解决方式,均能彻底解决生命周期问题,同时提升代码的可维护性和安全性:

方案1:将HashMap的键改为拥有所有权的String

把HashMap的键类型从&str改为String,这样可以存储拥有所有权的字符串,避免引用生命周期不匹配的问题。

修改点:

  1. 初始化HashMap时,将字符串字面量转为String
  2. 插入时直接把colour转为String

修改后的核心代码片段:

// 初始化HashMap,使用String作为键
let mut coloured_cubes = HashMap::from([
    ("red".to_string(), 0),
    ("green".to_string(), 0),
    ("blue".to_string(), 0),
]);

// 处理颜色插入时,直接转换为String
if count > coloured_cubes[colour] {
    coloured_cubes.insert(colour.to_string(), count);
}

方案2:使用枚举类型表示颜色(推荐)

定义一个枚举类型来表示所有合法颜色,这样不仅能解决生命周期问题,还能实现类型安全,提前在编译期检查非法颜色,减少运行时panic。

修改后的完整代码:

use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use std::convert::TryFrom;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum Colour {
    Red,
    Green,
    Blue,
}

impl TryFrom<&str> for Colour {
    type Error = &'static str;

    fn try_from(s: &str) -> Result<Self, Self::Error> {
        match s {
            "red" => Ok(Colour::Red),
            "green" => Ok(Colour::Green),
            "blue" => Ok(Colour::Blue),
            _ => Err("invalid colour"),
        }
    }
}

fn main() {
    let mut coloured_cubes = HashMap::from([
        (Colour::Red, 0),
        (Colour::Green, 0),
        (Colour::Blue, 0),
    ]);
    let mut game_nr = 0;
    let mut sum_of_powers = 0;

    let file = std::env::args().nth(1)
        .expect("Please specify a filename");

    if let Ok(lines) = read_lines(file) {
        for line in lines {
            if let Ok(mut cal_line) = line {
                game_nr += 1;
                // 重置颜色计数
                coloured_cubes.insert(Colour::Red, 0);
                coloured_cubes.insert(Colour::Green, 0);
                coloured_cubes.insert(Colour::Blue, 0);
                
                let prefix = format!("Game {game_nr}: ");
                if !cal_line.starts_with(&prefix) {
                    panic!("Line {game_nr} had a wrong prefix");
                }
                cal_line.replace_range(..prefix.len(), "");
                
                for game_part in cal_line.split("; ") {
                    for cube in game_part.split(", ") {
                        let cube_info: Vec<&str> = cube.split(' ').collect();
                        if cube_info.len() != 2 {
                            panic!("{prefix} wrong format '{cube}'");
                        }
                        let count = cube_info[0].parse::<i32>().unwrap();
                        let colour = Colour::try_from(cube_info[1])
                            .unwrap_or_else(|e| panic!("{prefix} illegal colour: {e}"));
                        
                        if count < 1 {
                            panic!("{} count should at least be 1 {:?}", prefix, cube_info);
                        }
                        
                        if count > coloured_cubes[&colour] {
                            coloured_cubes.insert(colour, count);
                        }
                    }
                }
                sum_of_powers += coloured_cubes[&Colour::Red] *
                    coloured_cubes[&Colour::Green] * coloured_cubes[&Colour::Blue];
            } else {
                panic!("Problem with line");
            }
        }
    } else {
        panic!("Did not get any lines!");
    }
    println!("ResultSum of powers: {sum_of_powers}");
}

fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

这种方案的优势:

  • 彻底消除生命周期问题,枚举是值类型,不存在引用失效风险
  • 编译期检查非法颜色,无需运行时contains_key判断
  • 代码可读性和可维护性更高,新增颜色只需扩展枚举和转换逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 16:10:42