Carlos Liam
Carlos Liam

Reputation: 3

Rust: Joining and iterating over futures' results

I have some code that iterates over objects and uses an async method on each of them sequentially before doing something with the results. I'd like to change it so that the async method calls are joined into a single future before being executed. The important bit below is in HolderStruct::add_squares. My current code looks like this:

use anyhow::Result;

struct AsyncMethodStruct {
    value: u64
}

impl AsyncMethodStruct {
    fn new(value: u64) -> Self {
        AsyncMethodStruct {
            value
        }
    }
    async fn get_square(&self) -> Result<u64> {
        Ok(self.value * self.value)
    }
}

struct HolderStruct {
    async_structs: Vec<AsyncMethodStruct>
}

impl HolderStruct {
    fn new(async_structs: Vec<AsyncMethodStruct>) -> Self {
        HolderStruct {
            async_structs
        }
    }
    async fn add_squares(&self) -> Result<u64> {
        let mut squares = Vec::with_capacity(self.async_structs.len());
        for async_struct in self.async_structs.iter() {
            squares.push(async_struct.get_square().await?);
        }
        let mut sum = 0;
        for square in squares.iter() {
            sum += square;
        }

        return Ok(sum);
    }
}

I'd like to change HolderStruct::add_squares to something like this:

use futures::future::join_all;
// [...]
impl HolderStruct {
    async fn add_squares(&self) -> Result<u64> {
        let mut square_futures = Vec::with_capacity(self.async_structs.len());
        for async_struct in self.async_structs.iter() {
            square_futures.push(async_struct.get_square());
        }
        let square_results = join_all(square_futures).await;
        let mut sum = 0;
        for square_result in square_results.iter() {
            sum += square_result?;
        }

        return Ok(sum);
    }
}

However, the compiler gives me this error using the above:

error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
  --> src/main.rs:46:20
   |
46 |             sum += square_result?;
   |                    ^^^^^^^^^^^^^^ the `?` operator cannot be applied to type `&std::result::Result<u64, anyhow::Error>`
   |
   = help: the trait `std::ops::Try` is not implemented for `&std::result::Result<u64, anyhow::Error>`
   = note: required by `std::ops::Try::into_result`

How would I change the code to not have this error?

Upvotes: 0

Views: 1779

Answers (1)

justinas
justinas

Reputation: 6857

for square_result in square_results.iter()

Lose the iter() call here.

for square_result in square_results

You seem to be under impression that calling iter() is mandatory to iterate over a collection. Actually, anything that implements IntoIterator can be used in a for loop.

Calling iter() on a Vec<T> derefs to slice (&[T]) and yields an iterator over references to the vectors elements. The ? operator tries to take the value out of the Result, but that is only possible if you own the Result rather than just have a reference to it.

However, if you simply use a vector itself in a for statement, it will use the IntoIterator implementation for Vec<T> which will yield items of type T rather than &T.

square_results.into_iter() does the same thing, albeit more verbosely. It is mostly useful when using iterators in a functional style, a la vector.into_iter().map(|x| x + 1).collect().

Upvotes: 1

Related Questions