Struggling with this basic borrow


#1

So I’ve read lots about borrowing, but there’s a super simple case I can’t get to work. I find myself doing this at several places in my code:

    Command::new("tmux")
        .args(...)

So I wanted to extract that to a function, where I’ll eventually add error-handling for the case where tmux isn’t found:

fn tmux(args: Vec<&str>) -> &mut Command {
    Command::new("tmux")
        .args(args)
}

This, of course, fails:

error: borrowed value does not live long enough
–> src/main.rs:41:5

41:5 is the first line of my function, Command::new.

How do I fix this? I generically understand borrowing, but sometimes have trouble adapting it to actual use.

Thanks.


Rust Objectives Observation
#2

Any references you return from a function need to be based on its arguments. Since you’re creating the Command inside the function here, you can’t return it as a reference.

You could do this though:

fn tmux(args: Vec<&str>) -> Command {
    let mut cmd = Command::new("tmux");
    cmd.args(args);
    cmd
}

#3

Thanks, that works. But I don’t understand why:

fn tmux(args: Vec<&str>) -> Command {
    let mut cmd= Command::new("tmux");
    cmd.args(args);
    cmd
}

works, but:

fn tmux(args: Vec<&str>) -> Command {
    let mut cmd= Command::new("tmux")
        .args(args);
    cmd
}

doesn't:

= note: expected type std::process::Command
found type &mut std::process::Command


When I initially constructed my `Command`, I think I assigned .args in a single call. I didn't realize there was a difference between these, and didn't even try the first. Is there some generic principle I can learn from this? If not, I imagine I'll have to break every multi-line statement into single calls until I get it working, which seems a bit fragile. :)

Thanks.

#4

The difference is in the function signatures of Command::new and Command::args. new returns an owned object, whereas args returns a mutable reference (and it also takes one as input). Your second example is the same as your original question. You’re storing a reference in cmd and then immediately returning it.


#5

args(…) returns a &mut Command, and then you try to return that, which brings you back to the original problem. Have you considered Command::new accepting args?

Edit: oops, I thought Command was a custom type. Maybe there should be a Command::with_args(…) convenience API, although perhaps it doesn’t carry its own weight.


#6

If you want to hand Ownership of cmd away (at the end of your fn tmux()), you’ll need a variable to hold the entire Command struct after .args() is done with it. That variable is the Owner inside the fn, and gives it away via “move-semantics” of the return value.

The difference between your two examples is in the type of cmd, which is different thanks to Type Inference.
In example one, cmd holds the return value of new(), so the actual Command itself.
In example two, cmd holds the reference to Command that is returned by .args(). The actual Command itself is Owned by an anonymous temporary variable (at least, that’s how I have learned to reason about it)

You can make this clearer by adding explicit type-annotations:

// 1) works
let mut  cmd : Command = Command::new("tmux");
//  add this ^^^^^^^^^
cmd.args(args);

// 2) shouldn't compile: type mismatch "&mut Command" instead of "Command"
let mut cmd : Command = Command::new("tmux").args(args);
// add this ^^^^^^^^^

The reason this works in main is that anonymous temp values like in 2 don’t get cleaned up until the closing } of their enclosing scope.
In main(), that closing bracket also ends the program, so the Command is alive for the entire program, and we don’t need a named variable because we’re not giving the Ownership away.
In your fn tmux(), that closing bracket ends the fn, so the anonymous variable containing Command should be cleaned up, but our reference in cmd should stay alive as return-value -> should be cleaned up, but also should be kept around -> conflict -> compiler error “value doesn’t live long enough”.


#7

I’m also struggling with a similar borrow concept. I’m using the builder pattern to wrap up the hyper HTTP client library with some common things I do throughout my code. I’m attempting to wrap that builder up with a few convenience methods in another structure.

struct Builder<'a> {
  client: &'a hyper::client::Client,
  base_url: Option<std::borrow::Cow<'a, str>>,
  // additional params elided to protect the brevity of this post...
}

impl<'a> Builder<'a> {
  pub fn new(client: &'a hyper::client::Client, method: hyper::method::Method) -> Builder {
    // set common headers and things.
    Builder {
      client: client,
      base_url: None,
    }
  }

  pub fn set_base_url<S>(&'a mut self, url: S) -> &'a mut Builder
    where S: Into<std::borrow::Cow<'a, str>
  {
    self.base_url = url.into();
    self
  }
}

Then I have a further wrapping of the builder to set the common base url. I’m attempting to write a method which wraps up some common settings when posting from a specific client implementation, yet still provide the same Builder API. However neither of the following approaches works:

  • Approach 1
impl<'a> MyClient<'a> {
  //... additional methods elided.
  pub fn post<S>(&self, path: S) -> &'a mut Builder
    where S: Into<std::borrow::Cow<'a, str>
  {
    Builder::new(self.client, hyper::method::Method::Post)
      .set_base_url(self.base_url.clone())
      .set_path(path)
  }
}

Results in:

  • Approach 2
impl<'a> MyClient<'a> {
  //... additional methods elided.
  pub fn post<S>(&self, path: S) -> &'a mut Builder
    where S: Into<std::borrow::Cow<'a, str>
  {
    let mut req = Builder::new(self.client, hyper::method::Method::Post);

    req.set_base_url(self.base_url.clone())
        .set_path(path);

    req
  }
}

Results in:

At this point I’m completely stumped… help?


#8

Look at the return-type of your post method:

pub fn post<S>(&self, path: S) -> &'a mut Builder
//                                ^

Your post method is returning a reference to the Builder.
But references can only point at things that are Owned by other variables.
Answer yourself this question: Who, in the current code, is holding the actual, non-reference/Owned req once the post-method is done?

If I may hazard a guess, you’re confusing yourself because you’re accepting references for the base_url. (Nice use of COW, by the way!)
base_url is indeed limited to a reference. You get it from the outside, so the outside determines who has Ownership.
However, the Builder you are creating is created by you, inside fn post. So the fn post must decide who gets Ownership after it is done, otherwise the compiler will Drop it at the closing bracket.

Edit:
Your current post signature is saying: "I’m returning a borrowed reference to someone else’s Builder, and that someone else promises to be around for 'a"
What you want to express is "I’m returning an Owned Builder that I just made, that contains references to someone-elses data (like base_url), that contained data is valid for 'a"
I’m not sure how exactly the syntax will end up for that though, but I hope this will set you on the right track. Please share if you get a working solution!


#9

post should return the Builder by value, and things should work. It’s basically the same issue as OP, just in a different context.


#10

@vitalyd I see now that my “Approach 2” code was not faithfully copied by me. (I probably should have been sleeping and not coding at the time :slight_smile:) The actual problematic code is:

  pub fn get<S>(&self, path: S) -> request::Builder                                                                                                                                                                                                                  
    where S: Into<Cow<'a, str>>                                                                                                                                                                                                                                      
  {                                                                                                                                                                                                                                                                  
    let mut req: request::Builder = request::Builder::new(self.client,                                                                                                                                                                                               
                                                          request::Method::Get);                                                                                                                                                                                     
                                                                                                                                                                                                                                                                     
    req.set_base_url(self.base_url.clone());                                                                                                                                                                                                                         
    req.set_path(path);                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                                     
    req                                                                                                                                                                                                                                                              
  }                                                                                                                                                                                                                                                                  

This code produces the following error output:


My confusion here is that I’m attempting to return an owned value, but the compiler is telling me that it doesn’t live log enough when I call a method on that owned value. At first I thought it might have to do with the method chaining so I broke that down. But now I’m lost. @juleskers Thank you for the well explained response; yes, you are correct I am trying to express that I’m returning a Builder containing borrowed references that live for 'a which the callee now owns.

Could the problem be with the internal data, and the compiler is pointing me in the wrong direction?

@juleskers, All credit for the Cow usage goes to Joe Wilm, I stole it from his blog post: http://blog.jwilm.io/from-str-to-cow/


#11

Thanks for sharing the blog post, I’ll add it to my to-read list.

As for the problem with the actual code: I’m not completely sure, but it seems the compiler cannot prove that your newly created builder ends before 'a is over. The builder holds a COW to path.
That COW may be a reference with lifetime 'a
If that potential reference to path goes out of scope before the builder does, that would cause a dangling pointer…
Again, I’m not sure, but that’s the only thing that comes to mind.


#12

Ok, I thought I must be missing something - sorry about that.

Here’s what I think is happening. You’re creating a Builder whose lifetime parameter is tied to MyClient. The lifetime of MyClient can be shorter than 'a. Your set_base_url() requires a mutable borrow of Builder of 'a scope, which is potentially longer than lifetime of Builder as mentioned above. Compiler is telling you it can’t borrow the Builder that long (because that’s the requirement you specified).

Have you tried removing 'a from &'a mut self in the set functions?


#13

@vitalyd Thank you for the suggestion about the &'a. The solution was in that direction.

// Builder functions.
pub fn set_base_url<S>(&mut self, s: S) -> &mut Builder<'a>
  where S: Into<std::borrow::Cow<'a, str>>
{
  self.base_url = Some(path.into());
  self
}

pub fn set_path<S>(&mut self, s: S) -> &mut Builder<'a>
  where S: Into<std::borrow::Cow<'a, str>>
{
  self.path = Some(path.into())
  self
}

// ... Meanwhile in the Client wrapper code...
pub fn get<S>(&self, path: S) -> Builder
  where S: Into<Cow<'a, str>>
{
  let mut req = Builder::new(self.client, hyper::method::Method::Get);
  
  req.set_base_url(self.base_url.clone())
    .set_path(path)

  req
}

@vitalyd and @juleskers thank you for the help and discussion.


#14

You’re welcome, thanks for sharing your solution so that others may learn from it!


#15

Thank you all, for all of this.

Based on this discussion, I was able to complete my first significant
Rust project
. I’m a reasonably
competent (albeit not perfect) software developer, so it bothered me how
much I struggled with the Rust compiler. Even reading how some of you,
who now seem successful, struggled with basic compiler errors helped me
to feel better about my own struggles.

So, thanks a bunch. All of this was very helpful in different ways.


#16

I’m happy someone was able to learn something from my struggles. @nolan thank you for starting the topic.