英文:
How to execute a method on a struct for each instance in a collection of structs until an error is encountered in Rust?
问题
我正在尝试运行LogCommand
结构体的每个实例的execute
方法,当遇到错误时停止。
use std::pin::Pin;
use std::future::Future;
use anyhow::Error;
use futures::future::ok;
use futures::stream::{TryStream, TryStreamExt};
pub struct LogCommand {
message: String,
}
impl LogCommand {
fn new(message: String) -> LogCommand {
LogCommand { message }
}
fn execute(&self) -> Pin<Box<dyn Future<Output = Result<bool, Error>>>> {
println!("{}", self.message);
Box::pin(ok(true))
}
}
fn main() {
let mut structs: Vec<LogCommand> = Vec::new();
structs.push(LogCommand::new("Test1".to_string()));
structs.push(LogCommand::new("Test2".to_string()));
structs.push(LogCommand::new("Test3".to_string()));
run_all_methods(structs);
}
async fn run_all_methods(logCommands: Vec<LogCommand>) -> Result<bool, Error> {
futures::stream::iter(logCommands).try_for_each(|i| i.execute()).await
}
我遇到的错误如下:
the method `try_for_each` exists for struct `Iter<IntoIter<LogCommand>>`, but its trait bounds were not satisfied
the following trait bounds were not satisfied:
`futures::stream::Iter<std::vec::IntoIter<LogCommand>>: TryStream`
which is required by `futures::stream::Iter<std::vec::IntoIter<LogCommand>>: TryStreamExt`
`&futures::stream::Iter<std::vec::IntoIter<LogCommand>>: TryStream`
which is required by `&futures::stream::Iter<std::vec::IntoIter<LogCommand>>: TryStreamExt`
`&mut futures::stream::Iter<std::vec::IntoIter<LogCommand>>: TryStream`
which is required by `&mut futures::stream::Iter<std::vec::IntoIter<LogCommand>>: TryStreamExt`rustcClick for full compiler diagnostic
iter.rs(9, 1): doesn't satisfy `_: TryStreamExt`
iter.rs(9, 1): doesn't satisfy `_: TryStream`
你有什么想法,我如何运行每个方法并在遇到错误时停止,而不直接使用for循环?(我被要求以更函数式的方式编写这段代码。)
英文:
I am trying to run the execute
method for each instance of the struct LogCommand
that I have in a collection of structs, stopping when an error is encountered.
use std::pin::Pin;
use std::future::Future;
use anyhow::Error;
use futures::future::ok;
use futures::stream::{TryStream, TryStreamExt};
pub struct LogCommand {
message: String,
}
impl LogCommand {
fn new(message: String) -> LogCommand {
LogCommand { message }
}
fn execute(&self) -> Pin<Box<dyn Future<Output = Result<bool, Error>>>> {
println!("{}", self.message);
Box::pin(ok(true))
}
}
fn main() {
let mut structs: Vec<LogCommand> = Vec::new();
structs.push(LogCommand::new("Test1".to_string()));
structs.push(LogCommand::new("Test2".to_string()));
structs.push(LogCommand::new("Test3".to_string()));
run_all_methods(structs);
}
async fn run_all_methods(logCommands: Vec<LogCommand>) -> Result<bool, Error> {
futures::stream::iter(logCommands).try_for_each(|i| i.execute())
}
The error I am getting is as follows:
the method `try_for_each` exists for struct `Iter<IntoIter<LogCommand>>`, but its trait bounds were not satisfied
the following trait bounds were not satisfied:
`futures::stream::Iter<std::vec::IntoIter<LogCommand>>: TryStream`
which is required by `futures::stream::Iter<std::vec::IntoIter<LogCommand>>: TryStreamExt`
`&futures::stream::Iter<std::vec::IntoIter<LogCommand>>: TryStream`
which is required by `&futures::stream::Iter<std::vec::IntoIter<LogCommand>>: TryStreamExt`
`&mut futures::stream::Iter<std::vec::IntoIter<LogCommand>>: TryStream`
which is required by `&mut futures::stream::Iter<std::vec::IntoIter<LogCommand>>: TryStreamExt`rustcClick for full compiler diagnostic
iter.rs(9, 1): doesn't satisfy `_: TryStreamExt`
iter.rs(9, 1): doesn't satisfy `_: TryStream`
Any idea on how I might go about running each of these methods and stopping when an error is encountered, without directly using a for loop? (I have been asked to write this in a more functional manner.)
答案1
得分: 3
错误的原因是try_for_each()
是TryStream
的方法,而futures::stream::iter(logCommands)
不是Result
的流,而是LogCommand
的流。Result
还没有出现,因为execute()
还没有完成。
要获取Result
的流,可以使用StreamExt::then()
,然后使用其中一个TryStream
操作来获取完成或错误。
但是你没有说明你想要对这些bool
做什么。如果你实际上不想要任何数据,可以将其替换为()
并进行收集:
use futures::StreamExt;
...
impl LogCommand {
fn new(message: String) -> LogCommand {
LogCommand { message }
}
fn execute(&self) -> Pin<Box<dyn Future<Output = Result<(), Error>>>> {
println!("{}", self.message);
Box::pin(ok(()))
}
}
async fn run_all_methods(log_commands: Vec<LogCommand>) -> Result<(), Error> {
futures::stream::iter(log_commands)
.then(|i| i.execute())
.try_collect()
.await
}
如果你想以某种方式组合这些bool
(逻辑与或逻辑或),那么也许try_fold()
会适合。如果你想要在bool
具有特定值时提前退出,将其转换为错误。
英文:
The reason for the error is that try_for_each()
is a method for TryStream
s — streams of Result
s — but futures::stream::iter(logCommands)
is not a stream of Result
s but of LogCommand
s. The Result
s haven't appeared yet because execute()
hasn't been done yet.
To get the stream of Result
s, you can use StreamExt::then()
, and then to get the completion or error you can use one of the TryStream
operations.
But you haven't said what you want to do with the bool
s. If you don't actually want any data there, you can replace it with ()
and collect:
use futures::StreamExt;
...
impl LogCommand {
fn new(message: String) -> LogCommand {
LogCommand { message }
}
fn execute(&self) -> Pin<Box<dyn Future<Output = Result<(), Error>>>> {
println!("{}", self.message);
Box::pin(ok(()))
}
}
async fn run_all_methods(log_commands: Vec<LogCommand>) -> Result<(), Error> {
futures::stream::iter(log_commands)
.then(|i| i.execute())
.try_collect()
.await
}
If you want to combine the bool
s in some way (logical AND or OR), then maybe try_fold()
will suit. If you want to exit early if the bool has a particular value, convert it to an error.
答案2
得分: 1
你可以阅读Kevin Reids的答案来了解为什么你的代码不能正常工作。但是既然我已经在看到那个答案之前编写了代码,我也会为run_all_methods
添加我的方法:
async fn run_all_methods(logCommands: Vec<LogCommand>) -> Result<bool, Error> {
logCommands
.iter()
.map(|c| c.execute())
.collect::<FuturesUnordered<_>>()
.try_fold(false, |acc, b| ok(acc || b))
.await
}
我选择了在尽可能长时间内保持在Iterator
领域而不是立即创建一个Stream
的选项,因为我认为它们比流更常用,所以编译器更有可能对其进行优化,并且你可以获得为它们实现的所有功能(尽管大多数在Stream
中都有复制,但该API感觉不太完善和完整)。
在将结构体映射为Futures
之后,我将其收集到FuturesUnordered
中,不过根据需求,它也可以是FuturesOrdered
,这样我就可以对从Stream
中产生的值有更多的控制,而不仅仅是使用stream::iter
。
由于你没有指定你想要对所有的布尔值做什么,我选择了对它们进行逻辑或操作。对于一个集合,你可以用try_collect
替换try_fold
,并将返回类型中的bool
替换为Vec<bool>
,对于早期返回,你可以在折叠中返回Err
变体而不是Ok
。
英文:
You can read why your code doesn't work as is in Kevin Reids answer. But since I had already written the code when I saw that I'll add my approach for run_all_methods
as well:
async fn run_all_methods(logCommands: Vec<LogCommand>) -> Result<bool, Error> {
logCommands
.iter()
.map(|c| c.execute())
.collect::<FuturesUnordered<_>>()
.try_fold(false, |acc, b| ok(acc || b))
.await
}
Instead of immediately creating a Stream
I went for the option of staying in Iterator
land as long as possible, since they're used quite more frequently than streams I believe that route is more likely to be optimized for in the compiler and you get all the bells and whistles that are implemented for them (granted most are replicated in Stream
s but that API doesn't feel quite as polished & complete)
After mapping the structs to Futures
I collect into a FuturesUnordered
though depending on the needs it might as well have been a FuturesOrdered
giving me a little more clontrol over how values are produced from the Stream
than the stream::iter
gives.
Since you haven't specified what you want to do with all the bools I've opted for a disjunction of them. For a collection you'd replace the try_fold
with try_collect
and switch bool
with Vec<bool>
in the return type, for an early return you'd return an Err
variant instead of Ok
in the fold.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论