Herman J. Radtke III

Strategies for solving 'cannot move out of' borrowing errors in Rust

Written by Herman J. Radtke III on 09 Jun 2015

The rules around references and borrowing in Rust are fairly straight-forward. Given an owned variable, we are allowed to have as many immutable references to that variable as we want. Rust defaults to immutability, so even functions like trim are written in such a way that the result is a reference to the original string:

The compiler knows that trimmed_name is a reference to name. As long as trimmed_name is still in scope, the compiler will not let us pass name to a function, reassign it or do any other move operation. We could clone() the name variable and then trim it, but we really just want to let the compiler know when we are done borrowingname. The key word here is scope. If the reference to name goes out of scope, the compiler will let us movename because it is no longer being borrowed. Let us wrap the call to trim() in curly braces to denote a different scope.

That is simple enough, but let us take it a step further. Suppose we wanted to get back the length of the trimmed string from within our scope. If we do that inside our curly braces, then trimmed_name_len will no longer exist once we leave that scope.

fnmain(){letname=" Herman ".to_string();{lettrimmed_name=name.trim();lettrimmed_name_len=trimmed_name.len();}println!("Length of trimmed string is {}",trimmed_name_len);// no such variable errorletowned_name=name;}

Strategies

There are a few ways to deal with this. They all look pretty similar, but have different trade-offs. We can return the value from a scoped block of code:

This is a cheap and quick way to force the reference to go out of scope. It does not require us to specify parameters or their types nor does it require us to specify the return type. It is not reusable though. We can get some more reuse if we use an anonymous function (or closure):

A closure requires us to specify parameters and their types, but makes specifying the return type optional. The way this is written, the anonymous function f is only usable within the function scope. If we want complete reusuability we can use a normal function:

These strategies only work if we are calling immutable functions. We are temporarily keeping the reference to get some other peice of information. This works really well that information is something like implements the Copy trait, such as numbers or booleans. If we wanted to do something like remove all spaces on a string like "H e r m a n" then we are mutating the string. We would have to call name.clone() in order to later move the original name variable.

Closure Without Parameters

You may have wondered if we really did have to specify parameters when using a closure. If we try to access the name variable from within the closure, it will create a reference during compile time. That reference will continue to exist, even if we try to remove the closure f from scope. Example:

Real World Example

The above examples are pretty contrived. However, you will run into this when you are breaking down functions into smaller parts. In this below example, I was using a find_matches function that required an input of type &str. Given a PathBuf, I needed to call the immutable file_name() method on it and then convert it to a &str by calling to_str() before calling find_matches(file_name). In order to return a tuple of (p, matches), I had to make sure reference created by file_name was out of scope. I chose to use a function, but could have use curly braces or a closure as we discussed above.