首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何从运行多次的move FnMut闭包中读取文件?

如何从运行多次的move FnMut闭包中读取文件?
EN

Stack Overflow用户
提问于 2021-06-15 14:18:32
回答 1查看 261关注 0票数 2

我正在使用glutin,所以我的程序的主循环有一个移动闭包,我试图用rodio机箱播放一个音频文件。使用下面的代码,一切正常工作,每次程序循环时,我都会听到一声嘟嘟声:

代码语言:javascript
复制
...
let sink: rodio::Sink = ...;
event_loop.run(move |event, _, control_flow | {
    let sound_file = File::open("beep.ogg").unwrap();
    let buf_wrap = BufReader::new(sound_file);
    let source = rodio::Decoder::new(buf_wrap).unwrap();
    sink.append(source);
    ...
});
...

但是,这非常慢,因为每次打开同一个文件循环时都会打开它,所以我尝试用以下方法修复它:

代码语言:javascript
复制
...
let sound_file = File::open("beep.ogg").unwrap();
event_loop.run(move |event, _, control_flow | {
    let buf_wrap = BufReader::new(sound_file);
    ...
});
...

但是现在编译器提供了以下错误消息:

代码语言:javascript
复制
error[E0507]: cannot move out of `sound_file`, a captured variable in an `FnMut` closure
  --> src/lib.rs:86:33
   |
83 |     let sound_file = File::open("beep.ogg").unwrap();
   |         ---------- captured outer variable
...
86 |         let buf_wrap = BufReader::new(sound_file);
   |                                       ^^^^^^^^^^ move occurs because `sound_file` has type `File`, which does not implement the `Copy` trait

我试着修复这个问题已经有一段时间了,但没有成功,任何洞察力都会受到极大的赞赏。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-06-15 16:27:05

问题

基本上,当前的问题是rodio::Decoder::new使用它读取的值(实际上,它已经被BufReader::new使用了)。因此,如果您有一个循环或一个可以多次调用的闭包,那么每次都必须得到一个新的值。这是File::open在您的第一个代码片段中所做的事情。

在第二个代码片段中,您只创建一次File,然后尝试多次使用它,这是Rust的所有权概念阻止您这样做的。

还请注意,遗憾的是,在rodio中使用引用并不是一种选择,因为解码器必须是'static (例如,请参见绑定在S上的Sink::append特性)。

解决方案

如果您认为您的文件系统有点慢,并且希望对其进行优化,那么您可能实际上希望预先读取整个文件( File::open不需要这样做)。这样做还应该为您提供一个可以克隆的缓冲区(例如Vec<u8>),从而允许重复创建可由Decoder使用的新值。下面是一个这样做的示例:

代码语言:javascript
复制
use std::io::Read;
let sink: rodio::Sink = /*...*/;
// Read file up-front
let mut data = Vec::new();
let mut sound_file = File::open("beep.ogg").unwrap();
sound_file.read_to_end(&mut data).unwrap();

event_loop.run(move |event, _, control_flow | {
    // Copies the now in-memory file content
    let cloned_data = data.clone();
    // Give the data a `std::io::Read` impl via the `std::io::Cursor` wrapper
    let buffered_file = Cursor::new(cloned_data);
    let source = rodio::Decoder::new(buffered_file).unwrap();
    sink.append(source);
});

然而,在我个人使用rodio的经验中,在Decoder中仍有相当多的处理工作,所以我也会预先进行解码,并使用rodio::source::Buffered包装器,如下所示:

代码语言:javascript
复制
use std::io::Read;
let sink: rodio::Sink = /*...*/;
// Read & decode file
let sound_file = File::open("beep.ogg").unwrap();
let source = rodio::Decoder::new(file).unwrap();
// store the decoded audio in a buffer
let source_buffered = source.buffered();

// At least in my version, this buffer is lazyly initialized,
// So, maybe, you also want to initialize it here buffer, e.g. via:
//source_buffered.clone().for_each(|_| {})

event_loop.run(move |event, _, control_flow | {
    // Just copy the in-memory decoded buffer and play it
    sink.append(source_buffered.clone());
});

如果在多线程环境中使用这种方法,或者就像静力学一样,也可以使用lazy_static机箱使这些rodio::source::Buffered实例在整个程序中可用,同时只进行一次初始化。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67988070

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档