0

I have been trying to make this function that takes in a list of IPs (via clientconfig stuct) and created all clients for the addresses. It should then return these so that I can use them. But I keep getting lifetime errors. I have been trying to understand lifecycles in Rust but I am stuck on this. I tried:

  • Using an 'out' variable like in the example below.
  • Normal return type
  • Encapsulating in a struct, and setting a property via &mut self.

I just can not get this to work, the current error i am getting is:

> error[E0597]: `completion_queues` does not live long enough
   --> src/rdma_client.rs:116:26
    |
116 |                         &completion_queues[i],
    |                          ^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
123 |         }
    |         -
    |         |
    |         `completion_queues` dropped here while still borrowed
    |         borrow might be used here, when `out` is dropped and runs the `Drop` code for type `Vec`
    |
    = note: values in a scope are dropped in the opposite order they are defined

I am running inside Tokio if that matters. The struct clientconfig and client are mine, but protectiondomain and context are from the RDMA library.

pub async fn connect_to_clients<'a>(
    config: ClientConfig,
    ctx: Arc<Context>,
    pd: Arc<ProtectionDomain<'_>>,
    mut out: Vec<Client<'a>>,
) {
    info!("Running client mode.");

    let completion_queues: Vec<CompletionQueue> = config
        .targets
        .iter()
        .enumerate()
        .map(|(i, _)| ctx.create_cq(1, i.try_into().unwrap()).unwrap())
        .collect();

    let mut i = 0;
    for c in config.targets {
        out.push(Client::new(&pd, &completion_queues[i], c.clone(), config.frame_size).await);
        i += 1;
    }
}

Config.rs:

use serde::Deserialize;

#[derive(Deserialize, Debug)]
pub struct ClientConfig {
  pub targets: Vec<String>,
  pub frame_size: usize,
}

and client.rs:

pub struct Client<'a> {
  qp: QueuePair<'a>,
  memory_manager: MemoryManager,
}

finally MemoryManager:

pub struct MemoryManager {
    pub mr: Arc<Mutex<MemoryRegion<u8>>>,
}
user2722968
  • 13,636
  • 2
  • 46
  • 67
  • 2
    Please post the full error from `cargo check`, not your IDE (you can click for full diagnostic). Also, please put a [Minimal Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example). – Chayim Friedman Apr 18 '23 at 11:10
  • 1
    `completion_queues` is a local variable of the function `connect_to_clients`. It will be de-allocated when that function exits and the variable goes out-of-scope, so you cannot return a reference to it out of the function whether you're returning it directly or putting it in a data structure or anything else. Since you say you own client (which I take to mean you can change the code) and you apparently don't use those values anywhere else have `Client::new` take an *owned* value rather than a reference for that argument. – Jared Smith Apr 18 '23 at 11:36
  • I would also *probably* make the function return an owned Vec that it constructs rather than mutate a param (if you want mutation I'd make it an impl method that mutates `self`). – Jared Smith Apr 18 '23 at 11:41
  • As it stands, the `out` argument does not return anything to the caller because it's passed in by value... you probably meant to pass as `&mut out:...` (pass by mutable borrow) and not just `mut` (pass by value, but this function can modify) `mut out: Vec>,` – Colin D Bennett Apr 18 '23 at 17:43

1 Answers1

1

completion_queues is a local variable of the function connect_to_clients. It will be de-allocated when that function exits and the variable goes out-of-scope, so you cannot return a reference to it out of the function whether you're returning it directly or putting it in a data structure or anything else.

Since you say you own client (which I take to mean you can change the code) and you apparently don't use those values anywhere else have Client::new take an owned value rather than a reference for that argument:

impl Client {
    pub async fn new(
        &mut self, 
        pd: &Arc<ProtectionDomain<'_>>, 
        cq: CompletionQueue, // NOT a reference
        config_target: Whatever,
        frame_size: Whatever,
    ) -> Self { ... }
}

You can probably can/should restructure that function to return a collected Vec instead of mutating a param unless you really need it to, although that's harder to show without a complete example of what you're doing.

Jared Smith
  • 19,721
  • 5
  • 45
  • 83