How to access a variable after it is passed to a function?

I want to create a middleware of actix_web. I followed sample and come up below code that works fine.

fn call(&mut self, req: ServiceRequest) -> Self::Future {
    println!("AuthZ middleware started for request: {}.", req.path());
    let fut = self.service.call(req);
    Box::pin(async move {
        let res = fut.await?;
        println!("AuthZ middleware ended.");
        Ok(res)
    })
}

When I want to change the second println! to output the request path as well. The code becomes as below

fn call(&mut self, req: ServiceRequest) -> Self::Future {
    println!("AuthZ middleware started for request: {}.", req.path());
    let fut = self.service.call(req);
    Box::pin(async move {
        let res = fut.await?;
        println!("AuthZ middleware ended: {}.", req.path());
        Ok(res)
    })
}

It started to report an error when I used the req after it was moved into self.service.call. I tried to follow some other samples that shows I can pass a value in as reference to make sure the value is borrowed and the ownership will be returned after the call. So, I tried to change it to be self.service.call(&req). This change gave me another error that says somethig like "expected a structure but found a reference, consider removing the borrow".

Based on above experience, I have several questions.

  1. In actix_web, how do I solve this issue if I want to access request info after previous middleware is done? I am guessing maybe something in the return value of self.service.call() may helpful, but I am not sure.

  2. In general, if I want to use a 3rd party function that only accepts a parameter and must take ownership of the value. I also want to access the value after the call to 3rd party function. What should I do?

Thanks a lot for any suggestions.

You’ll need to clone() the structure, or at least the parts you care about, before calling the function.

If you need it after and can't change the function itself to accept a reference. then like he said you need to clone it

would become

self.service.call(req.clone());

Most things usually have a clone() implementation.

What is the type of the service, and where does it come from? The Service trait itself is very generic, it looks like different implementations could use references rather than consuming the entire request, but it would depend on the middleware implementation. Whether the request is available on the response will also depend on the which type that the Service is.

In your specific example, you could use the format! macro to format the logging message so that you don’t need the request after the service call, though that’s not terribly ideal.

Directly: you can't.

After something is moved to a function, it's gone for good. That function got full exclusive ownership of the object. That's not even a theoretical limitation: that function could have destroyed the object right away, so it may not even exist after the function call. There's no garbage collector in Rust, so Rust can't make objects live longer for you, you need to change what the code does.

Typical workarounds:

  1. Get the data you need before you give the object away. In your case let path = req.path().to_owned(); to keep your own copy of the path, independent of req.

  2. Some functions allow arguments to be passed either as owned or borrowed. e.g. std::fs::write(path, data) and std::fs::write(&path, &data) are both valid (they have AsRef<Path> as argument type). If you can temporarily lend an object to a function instead of passing ownership, you will be able to use it later.

  3. If these are your functions, and you don't want either of previous solutions, you can wrap object in Arc<O> (read-only) or Arc<Mutex<O>> (read-write) to be able to clone references to it and have it "owned" in multiple places at once.

2 Likes

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.