如何使用数据并返回对它的引用?

问题描述 投票:0回答:1

背景:我正在研究一种非常快速的算法来解决 2023 年代码降临第 8 天第 2 部分的挑战,无需最小公倍数方法(这意味着不可能,因此需要优化)。

我的解决方案之一在内部与

HashMap
配合使用。作为键和值,它们包含字符串或对输入数据中存在的字符串的引用(将其视为一个大字符串)。

所以基本上,我正在分解输入数据并将其分布在一堆派生数据结构中。

我现在可以使用 .to_owned()

复制
这些位,或者仅使用对原始输入中的部分的引用,这将运行时间从 2 分 12 秒缩短到 1 分 38 秒。

我的麻烦是,如果不将数据结构的生命周期与输入数据引用的生命周期联系起来,我就无法使用快速引用方法,这意味着调用者必须处理这个问题。

我更喜欢的是使用输入数据,这样调用者就可以忘记它,并且仍然在内部使用引用。但这是行不通的。

让一些虚拟代码突出显示问题:

use std::fs;

/**
 * Works fine, but needs to duplicate data, which makes it a bit slower
 */
pub fn read_file_and_analyze_little_slow(file_name: &str) -> (String, Vec<String>) {
    let content = fs::read_to_string(file_name).expect("Could not read the file you told me to analyze");
    let lines: Vec<String> = content.split("\n").map(|x| { x.to_owned() }).collect();

    (content, lines)
}

/**
 * I wish I could do this, but the read content of the file is dropped
 * at the end of the file (or so the borrow checker thinks), invalidating
 * the references
 */
pub fn read_file_and_analyze_fast(file_name: &str) -> (String, Vec<&str>) {
    let content = fs::read_to_string(file_name).expect("Could not read the file you told me to analyze");
    let lines: Vec<&str> = content.split("\n").collect();

    // Would need to make content live on somehow, so the references remain valid
    (content, lines) // Error: cannot move out of `content` because it is borrowed
}

/**
 * This works super well, but we've just transferred the issue to the calling function,
 * because now our result is tied to the lifetime of input string, meaning the parent
 * function can't pass it as _its_ result, unless it also works with lifetimes
 */
pub fn read_file_and_analyze_unwieldy<'a>(already_read_file: &'a str) -> Vec<&'a str> {
    already_read_file.split("\n").collect()
}

当然还有更多的变体,比如将输入作为

String
传入,这会更好地与问题标题对齐,但据我所知并没有对问题产生实际的影响。

我正在寻找使

read_file_and_analyze_fast
的 API 正常工作的解决方案。我怀疑这是可能可能的,但可能需要
unsafe
,我以前没有使用过。

这是一个非常有趣的问题,因此我们也将不胜感激一些处理此类问题的一般指导

rust lifetime borrow-checker unsafe
1个回答
0
投票

这是

String::leak
可能合适的情况之一。 这会消耗
String
(不释放它拥有的堆分配)并返回对整个字符串的切片的引用,但具有
'static
生命周期。

pub fn read_file_and_analyze_fast(file_name: &str) -> Vec<&'static str> {
    let content = std::fs::read_to_string(file_name)
        .expect("Could not read the file you told me to analyze")
        .leak();

    let lines: Vec<&str> = content.split("\n").collect();

    lines
}

请注意,我将返回类型更改为

Vec<&'static str>
,以明确包含在
Vec
中的字符串切片具有
'static
生命周期。 如果您不更改此设置,它们将被限制为与输入切片引用相同的生命周期
file_name
(也可以是
'static
,但这在这里并不重要)。

另一种方法是使用

include_str!
,因为输入文件每次都相同,这是一个宏,可扩展为通过在编译时读取文件
 的内容而获得的 
&'static str(这是我在《Advent of Code》工作时所做的。)

© www.soinside.com 2019 - 2024. All rights reserved.