以下代码从stdin读取空格分隔的记录,并将逗号分隔的记录写入stdout . 即使使用优化的构建,它也相当慢(大约是使用的两倍,比如awk) .
use std::io::BufRead;
fn main() {
let stdin = std::io::stdin();
for line in stdin.lock().lines().map(|x| x.unwrap()) {
let fields: Vec<_> = line.split(' ').collect();
println!("{}", fields.join(","));
}
}
一个明显的改进是使用 itertools
加入而不分配向量( collect
调用导致分配) . 但是,我尝试了一种不同的方法:
fn main() {
let stdin = std::io::stdin();
let mut cache = Vec::<&str>::new();
for line in stdin.lock().lines().map(|x| x.unwrap()) {
cache.extend(line.split(' '));
println!("{}", cache.join(","));
cache.clear();
}
}
此版本尝试反复使用相同的向量 . 不幸的是,编译器抱怨:
error: `line` does not live long enough
--> src/main.rs:7:22
|
7 | cache.extend(line.split(' '));
| ^^^^
|
note: reference must be valid for the block suffix following statement 1 at 5:39...
--> src/main.rs:5:40
|
5 | let mut cache = Vec::<&str>::new();
| ^
note: ...but borrowed value is only valid for the for at 6:4
--> src/main.rs:6:5
|
6 | for line in stdin.lock().lines().map(|x| x.unwrap()) {
| ^
error: aborting due to previous error
这当然有意义: line
变量仅在 for
循环的主体中存活,而 cache
在迭代中保持指针 . 但是这个错误对我来说仍然是虚假的:因为每次迭代后缓存都是 clear
,所以不能保留对 line
的引用,对吧?
我怎样才能告诉借阅检查员?
3 回答
唯一的方法是使用transmute将
Vec<&'a str>
更改为Vec<&'b str>
.transmute
是不安全的,如果忘记调用clear
,Rust不会引发错误 . 您可能希望在调用clear
之后将unsafe
块扩展到清除(没有双关语),代码返回到"safe land" .在这种情况下,Rust没有尝试做 . 不幸的是,
.clear()
不会影响如何检查.extend()
.cache
是"vector of strings that live as long as the main function",但在extend()
中调用're appending 1270129 , so that'是类型不匹配 . 对.clear()
的调用不会更改类型 .通常这种限时使用是通过制作一个长寿命的不透明对象来表示的,该对象通过借用具有正确生命周期的临时对象来访问其内存,如
RefCell.borrow()
给出一个临时的Ref
对象 . 这样做的实现有点涉及,并且需要不安全的方法来回收Vec
的内部存储器 .在这种情况下,替代解决方案可以是完全避免任何分配(
.join()
也分配)并通过Peekable
iterator包装器流式传输:顺便说一下:弗朗西斯回答
transmute
也很好 . 您可以使用unsafe
表示您知道自己在做什么并覆盖终身检查 .Itertools有.format()用于延迟格式化,它也会跳过分配字符串 .
(一个题外话,这样的东西是一个“安全的抽象”,在另一个答案的最小解决方案中:
)