背景:我正在研究一种非常快速的算法来解决 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
,我以前没有使用过。
这是一个非常有趣的问题,因此我们也将不胜感激一些处理此类问题的一般指导
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》工作时所做的。)