Return value lifetime and?

I am trying to get errors to propagate in a fairly complicated piece of code. I have created a simplified version of the code with the error bellow.

use anyhow::{Error, Result};
use async_channel::{unbounded, Sender};

#[derive(Clone)]
struct File {}
struct Comparitor {}

impl<'a> Comparitor {
  pub async fn call<I>(
    &self,
    src_files: &mut I,
    dest_files: &mut I,
    ret: &Sender<&'a mut File>,
  ) -> Result<()>
  where
    I: Iterator<Item = &'a mut File>,
  {
    let mut src_file: Option<&'a mut File> = src_files.next();
    let mut dest_file: Option<&'a mut File> = dest_files.next();

    ret.send(src_file.unwrap()).await?;

    // do something

    Ok(())
  }
}

#[tokio::main]
async fn main() -> Result<(), Error> {
  let mut comparitor = Comparitor {};

  let src_file = File {};
  let mut src_files = vec![src_file.clone()];
  let mut dest_files = vec![File {}];

  let mut src_iter = src_files.iter_mut();
  let mut dest_iter = dest_files.iter_mut();
  let (s, r) = unbounded::<&mut File>();
  let _ = comparitor.call(&mut src_iter, &mut dest_iter, &s).await;
  Ok(())
}

I get the following error.

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/bin/lifetime-test.rs:21:5
   |
21 |     ret.send(src_file.unwrap()).await?;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 8:6...
  --> src/bin/lifetime-test.rs:8:6
   |
8  | impl<'a> Comparitor {
   |      ^^
note: ...so that the types are compatible
  --> src/bin/lifetime-test.rs:21:5
   |
21 |     ret.send(src_file.unwrap()).await?;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected  `std::task::Poll<std::result::Result<_, async_channel::SendError<&mut File>>>`
              found  `std::task::Poll<std::result::Result<_, async_channel::SendError<&'a mut File>>>`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `async_channel::SendError<&mut File>` will meet its required lifetime bounds
  --> src/bin/lifetime-test.rs:21:38
   |
21 |     ret.send(src_file.unwrap()).await?;
   |                                      ^

error: aborting due to previous error

Any help would be greatly appreciated.

This is because Sender::send includes the item type in the error type, which means that it can't be converted into an anyhow error. Usually I would just recommend using Tokio's channel.

I had a look at the Tokio channel but that's blocking, at the moment I think I will go with

match ret.send(src_file.unwrap()).await {
  Err(SendError { .. }) => Err(anyhow!("Channel has been closed")),
  _ => Ok(()),
};

Tokio's channel is not blocking. Can you tell me what made you think it is blocking so we can fix the documentation?

I was looking at UnboundedSender. It looks like the bounded version is async but not the unbounded version?

Sending to an unbounded channel would never involve waiting, and async vs blocking only really makes sense for things that need to wait.

Yes, that makes sense, thanks for the help.

1 Like

OK, I spoke too soon, if I go to a bounded channel I am back to the same error as before.

use anyhow::{Error, Result};
use tokio::sync::mpsc::{channel, Sender};

#[derive(Clone, Debug)]
struct File {}
struct Comparitor {}

impl<'a> Comparitor {
  pub async fn call<I>(
    &self,
    src_files: &mut I,
    dest_files: &mut I,
    ret: &Sender<&'a mut File>,
  ) -> Result<()>
  where
    I: Iterator<Item = &'a mut File>,
  {
    let mut src_file: Option<&'a mut File> = src_files.next();
    let mut dest_file: Option<&'a mut File> = dest_files.next();

    ret.send(src_file.unwrap()).await?;

    // do something

    Ok(())
  }
}

#[tokio::main]
async fn main() -> Result<(), Error> {
  let mut comparitor = Comparitor {};

  let src_file = File {};
  let mut src_files = vec![src_file.clone()];
  let mut dest_files = vec![File {}];

  let mut src_iter = src_files.iter_mut();
  let mut dest_iter = dest_files.iter_mut();
  let (s, r) = channel::<&mut File>(1000);
  let _ = comparitor.call(&mut src_iter, &mut dest_iter, &s).await;
  Ok(())
}

WIth error

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/bin/lifetime-test.rs:21:5
   |
21 |     ret.send(src_file.unwrap()).await?;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 8:6...
  --> src/bin/lifetime-test.rs:8:6
   |
8  | impl<'a> Comparitor {
   |      ^^
note: ...so that the types are compatible
  --> src/bin/lifetime-test.rs:21:5
   |
21 |     ret.send(src_file.unwrap()).await?;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected  `std::task::Poll<std::result::Result<_, tokio::sync::mpsc::error::SendError<&mut File>>>`
              found  `std::task::Poll<std::result::Result<_, tokio::sync::mpsc::error::SendError<&'a mut File>>>`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `tokio::sync::mpsc::error::SendError<&mut File>` will meet its required lifetime bounds
  --> src/bin/lifetime-test.rs:21:38
   |
21 |     ret.send(src_file.unwrap()).await?;
   |                                      ^

error: aborting due to previous error

Ah, right, it does the same thing. But with a bit more thought, this is a good thing, because you should typically handle this kind of error, because it means that the receiver has been dropped, which is either an unreachable logic error or an expected runtime condition.

As this shouldn't happen I guess it should be handled with an .expect("channel should be open")

I would typically use a panic such as expect for logic errors that are supposed to be unreachable, yes.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.