• Light (default)

The Rust Programming Language

References and borrowing.

This is the second of three sections presenting Rust’s ownership system. This is one of Rust’s most distinct and compelling features, with which Rust developers should become quite acquainted. Ownership is how Rust achieves its largest goal, memory safety. There are a few distinct concepts, each with its own chapter:

  • ownership , the key concept
  • borrowing, which you’re reading now
  • lifetimes , an advanced concept of borrowing

These three chapters are related, and in order. You’ll need all three to fully understand the ownership system.

Before we get to the details, two important notes about the ownership system.

Rust has a focus on safety and speed. It accomplishes these goals through many ‘zero-cost abstractions’, which means that in Rust, abstractions cost as little as possible in order to make them work. The ownership system is a prime example of a zero-cost abstraction. All of the analysis we’ll talk about in this guide is done at compile time . You do not pay any run-time cost for any of these features.

However, this system does have a certain cost: learning curve. Many new users to Rust experience something we like to call ‘fighting with the borrow checker’, where the Rust compiler refuses to compile a program that the author thinks is valid. This often happens because the programmer’s mental model of how ownership should work doesn’t match the actual rules that Rust implements. You probably will experience similar things at first. There is good news, however: more experienced Rust developers report that once they work with the rules of the ownership system for a period of time, they fight the borrow checker less and less.

With that in mind, let’s learn about borrowing.

At the end of the ownership section, we had a nasty function that looked like this:

This is not idiomatic Rust, however, as it doesn’t take advantage of borrowing. Here’s the first step:

A more concrete example:

Instead of taking Vec<i32> s as our arguments, we take a reference: &Vec<i32> . And instead of passing v1 and v2 directly, we pass &v1 and &v2 . We call the &T type a ‘reference’, and rather than owning the resource, it borrows ownership. A binding that borrows something does not deallocate the resource when it goes out of scope. This means that after the call to foo() , we can use our original bindings again.

References are immutable, like bindings. This means that inside of foo() , the vectors can’t be changed at all:

will give us this error:

Pushing a value mutates the vector, and so we aren’t allowed to do it.

&mut references

There’s a second kind of reference: &mut T . A ‘mutable reference’ allows you to mutate the resource you’re borrowing. For example:

This will print 6 . We make y a mutable reference to x , then add one to the thing y points at. You’ll notice that x had to be marked mut as well. If it wasn’t, we couldn’t take a mutable borrow to an immutable value.

You'll also notice we added an asterisk ( * ) in front of y , making it *y , this is because y is a &mut reference. You'll need to use asterisks to access the contents of a reference as well.

Otherwise, &mut references are like references. There is a large difference between the two, and how they interact, though. You can tell something is fishy in the above example, because we need that extra scope, with the { and } . If we remove them, we get an error:

As it turns out, there are rules.

Here are the rules for borrowing in Rust:

First, any borrow must last for a scope no greater than that of the owner. Second, you may have one or the other of these two kinds of borrows, but not both at the same time:

  • one or more references ( &T ) to a resource,
  • exactly one mutable reference ( &mut T ).

You may notice that this is very similar to, though not exactly the same as, the definition of a data race:

There is a ‘data race’ when two or more pointers access the same memory location at the same time, where at least one of them is writing, and the operations are not synchronized.

With references, you may have as many as you’d like, since none of them are writing. However, as we can only have one &mut at a time, it is impossible to have a data race. This is how Rust prevents data races at compile time: we’ll get errors if we break the rules.

With this in mind, let’s consider our example again.

Thinking in scopes

Here’s the code:

This code gives us this error:

This is because we’ve violated the rules: we have a &mut T pointing to x , and so we aren’t allowed to create any &T s. It's one or the other. The note hints at how to think about this problem:

In other words, the mutable borrow is held through the rest of our example. What we want is for the mutable borrow by y to end so that the resource can be returned to the owner, x . x can then provide an immutable borrow to println! . In Rust, borrowing is tied to the scope that the borrow is valid for. And our scopes look like this:

The scopes conflict: we can’t make an &x while y is in scope.

So when we add the curly braces:

There’s no problem. Our mutable borrow goes out of scope before we create an immutable one. So scope is the key to seeing how long a borrow lasts for.

Issues borrowing prevents

Why have these restrictive rules? Well, as we noted, these rules prevent data races. What kinds of issues do data races cause? Here are a few.

Iterator invalidation

One example is ‘iterator invalidation’, which happens when you try to mutate a collection that you’re iterating over. Rust’s borrow checker prevents this from happening:

This prints out one through three. As we iterate through the vector, we’re only given references to the elements. And v is itself borrowed as immutable, which means we can’t change it while we’re iterating:

Here’s the error:

We can’t modify v because it’s borrowed by the loop.

Use after free

References must not live longer than the resource they refer to. Rust will check the scopes of your references to ensure that this is true.

If Rust didn’t check this property, we could accidentally use a reference which was invalid. For example:

We get this error:

In other words, y is only valid for the scope where x exists. As soon as x goes away, it becomes invalid to refer to it. As such, the error says that the borrow ‘doesn’t live long enough’ because it’s not valid for the right amount of time.

The same problem occurs when the reference is declared before the variable it refers to. This is because resources within the same scope are freed in the opposite order they were declared:

In the above example, y is declared before x , meaning that y lives longer than x , which is not allowed.

  • Light (default)

Learning Rust With Entirely Too Many Linked Lists

Let's take a crack at iterating this bad-boy.

IntoIter, as always, is going to be the easiest. Just wrap the stack and call pop :

But we have an interesting new development. Where previously there was only ever one "natural" iteration order for our lists, a Deque is inherently bi-directional. What's so special about front-to-back? What if someone wants to iterate in the other direction?

Rust actually has an answer to this: DoubleEndedIterator . DoubleEndedIterator inherits from Iterator (meaning all DoubleEndedIterator are Iterators) and requires one new method: next_back . It has the exact same signature as next , but it's supposed to yield elements from the other end. The semantics of DoubleEndedIterator are super convenient for us: the iterator becomes a deque. You can consume elements from the front and back until the two ends converge, at which point the iterator is empty.

Much like Iterator and next , it turns out that next_back isn't really something consumers of the DoubleEndedIterator really care about. Rather, the best part of this interface is that it exposes the rev method, which wraps up the iterator to make a new one that yields the elements in reverse order. The semantics of this are fairly straight-forward: calls to next on the reversed iterator are just calls to next_back .

Anyway, because we're already a deque providing this API is pretty easy:

And let's test it out:

Iter will be a bit less forgiving. We'll have to deal with those awful Ref things again! Because of Refs, we can't store &Node s like we did before. Instead, let's try to store Ref<Node> s:

So far so good. Implementing next is going to be a bit hairy, but I think it's the same basic logic as the old stack IterMut but with extra RefCell madness:

node_ref doesn't live long enough. Unlike normal references, Rust doesn't let us just split Refs up like that. The Ref we get out of head.borrow() is only allowed to live as long as node_ref , but we end up trashing that in our Ref::map call.

The function we want exists, and it's called [map_split][] :

Woof. Let's give it a try...

Ergh. We need to Ref::Map again to get our lifetimes right. But Ref::Map returns a Ref and we need an Option<Ref> , but we need to go through the Ref to map over our Option...

stares into distance for a long time

Oh. Right. There's multiple RefCells. The deeper we walk into the list, the more nested we become under each RefCell. We would need to maintain, like, a stack of Refs to represent all the outstanding loans we're holding, because if we stop looking at an element we need to decrement the borrow-count on every RefCell that comes before it.................

I don't think there's anything we can do here. It's a dead end. Let's try getting out of the RefCells.

What about our Rc s. Who said we even needed to store references? Why can't we just Clone the whole Rc to get a nice owning handle into the middle of the list?

Uh... Wait what do we return now? &T ? Ref<T> ?

No, none of those work... our Iter doesn't have a lifetime anymore! Both &T and Ref<T> require us to declare some lifetime up front before we get into next . But anything we manage to get out of our Rc would be borrowing the Iterator... brain... hurt... aaaaaahhhhhh

Maybe we can... map... the Rc... to get an Rc<T> ? Is that a thing? Rc's docs don't seem to have anything like that. Actually someone made a crate that lets you do that.

But wait, even if we do that then we've got an even bigger problem: the dreaded spectre of iterator invalidation. Previously we've been totally immune to iterator invalidation, because the Iter borrowed the list, leaving it totally immutable. However if our Iter was yielding Rcs, they wouldn't borrow the list at all! That means people can start calling push and pop on the list while they hold pointers into it!

Oh lord, what will that do?!

Well, pushing is actually fine. We've got a view into some sub-range of the list, and the list will just grow beyond our sights. No biggie.

However pop is another story. If they're popping elements outside of our range, it should still be fine. We can't see those nodes so nothing will happen. However if they try to pop off the node we're pointing at... everything will blow up! In particular when they go to unwrap the result of the try_unwrap , it will actually fail, and the whole program will panic.

That's actually pretty cool. We can get tons of interior owning pointers into the list and mutate it at the same time and it will just work until they try to remove the nodes that we're pointing at. And even then we don't get dangling pointers or anything, the program will deterministically panic!

But having to deal with iterator invalidation on top of mapping Rcs just seems... bad. Rc<RefCell> has really truly finally failed us. Interestingly, we've experienced an inversion of the persistent stack case. Where the persistent stack struggled to ever reclaim ownership of the data but could get references all day every day, our list had no problem gaining ownership, but really struggled to loan our references.

Although to be fair, most of our struggles revolved around wanting to hide the implementation details and have a decent API. We could do everything fine if we wanted to just pass around Nodes all over the place.

Heck, we could make multiple concurrent IterMuts that were runtime checked to not be mutable accessing the same element!

Really, this design is more appropriate for an internal data structure that never makes it out to consumers of the API. Interior mutability is great for writing safe applications . Not so much safe libraries .

Anyway, that's me giving up on Iter and IterMut. We could do them, but ugh .

Search code, repositories, users, issues, pull requests...

Provide feedback.

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly.

To see all available qualifiers, see our documentation .

  • Notifications

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"lifetime of borrowed pointer outlives lifetime of captured variable" error message very unclear #42574

@pnkfelix

jonas-schievink commented Jun 9, 2017

Sorry, something went wrong.

@Mark-Simulacrum

Mark-Simulacrum commented Jun 23, 2017

@nikomatsakis

nikomatsakis commented Jul 17, 2017

@nikomatsakis

nikomatsakis commented Jul 17, 2017 • edited

@pnkfelix

pnkfelix commented Nov 9, 2018 • edited

Pnkfelix commented nov 9, 2018, pnkfelix commented nov 29, 2018, pnkfelix commented dec 6, 2018, pnkfelix commented dec 19, 2018, pnkfelix commented jul 9, 2019.

@Centril

estebank commented Jul 18, 2019

Pnkfelix commented sep 20, 2019.

@crlf0710

rylev commented Jun 17, 2021 • edited

@rylev

Aaron1011 commented Oct 3, 2021

Estebank commented oct 4, 2021.

@bindsdev

estebank commented Nov 7, 2023

@estebank

No branches or pull requests

@nikomatsakis

07 Rustlings Move Semantics Part 2

From the Rustlings ReadMe: These exercises are adapted from pnkfelix 's Rust Tutorial -- Thank you Felix!!!

Further information

For this section, the book links are especially important.

  • Reference and borrowing

Move Semantics Part 2

We've already covered the first three exercises of Move Semantics in part one, in this episode we tackle the next three. Let's get started!

move_semantics4.rs

Our instructions here are to refactor our code so that instead of passing vec0 into the fill_vec function, the Vector gets created in the function itself and passed back to the main function.

move_semantics4.rs errors

Error's are telling us that on line 23, vec is not a value in addition it tells us that the function fill_vec() takes 0 arguments but one is being supplied. So let's remove the argument on line 12 from the fill_vec(vec0) .

After we do that we still get some errors, but they're different now.

The error's start point us to using generics with Vec<T> but our solution should be must simpler than that, since our instructions are to refactor our code to not create vec0 but to create a new vector in our fill_vec() function.

Let's see what happens if we simple remove the let vec0 = Vec::new(); line from our code but and add the let mut vec = Vec::new(); to our fill_vec() function. Hey guess what? It works. Below is the update code solution.

move_semantics4 solution

This is our output:

move_semantics5.rs

This looks like an easy one, no need to write anything we just need to re-order the sequence of the lines. Let's look at the errors and see if we can get any hints on what we need to do.

move_semantics5.rs errors

The Rust compiler tells us that we are borrowing x mutably too many times, so let's go step by step and see what is happening on each line.

  • We declare a mutable variable x and assign it the value 100 .
  • We create a mutable reference y that borrows x using &mut x .
  • The rules of borrowing state that you can have either one mutable reference or any number of immutable references to a value at a given time.
  • In this case, we already have y as a mutable reference to x , so we can't create another mutable reference z .
  • The code tries to dereference y using *y and add 100 to the value of x . This is invalid because y is still in scope and holds a mutable reference to x , and at this point, z also exists.
  • Similarly, when the code tries to dereference z using *z and add 1000 to the value of x , it violates the borrowing rules.

So how do we solve this? Let's try by dereferencing y before we try to borrow it again with z by moving the line *y += 100; above the 2nd attempt mutable borrow which is let z = &mut x; . Doing so should allow us to compile.

move_semantics5.rs solution

It compiles! We don't get an output because there is not println! statement instead we have an assert_eq! .

The code compiles successfully because it follows the borrowing rules in Rust. Here's a step-by-step explanation:

  • We start by declaring a mutable variable x and assigning it the value 100 .
  • We then create a mutable reference y that borrows x using &mut x . This allows us to modify x through y .
  • We dereference y using *y and add 100 to the value of x . This modifies x to 200 .
  • Next, we create another mutable reference z that also borrows x using &mut x . This is allowed because there are no other references to x at this point.
  • We dereference z using *z and add 1000 to the value of x . This modifies x to 1200 .
  • Finally, we use assert_eq! to check if x is equal to 1200 . Since the value of x is indeed 1200 , the assertion passes.

Let's move on to our final move_semantics exercise.

move_semantics6.rs

Our instructions are to not change anything but the references, so we'll look at the errors to get a better understanding as to where we are having issues.

move_semantics6.rs errors

Alright let's go down the list understanding what there errors are telling us and see how we can fix them.

here the compiler is telling us clearly where to look data does not implement the copy trait so we when we pass it through as a parameter in get_char(data), it becomes owned by get_char()

In the next batch of errors we get a suggestion about cloning, but we know that we can't change any of the code other than changing the references, so this is not the path we want to take.

We now get a message about the lifetime of a reference which we haven't covered yet so let's just keep this in mind for now, but again our task is to essentially just change how the functions handle ownership. So let's go back and look at the functions in the code:

Looking at these two code blocks it looks straightforward, it's clear that we must change where the & symbol is being used and essentially swap positions in each function to this:

Once we've done this we get new error's but it should be pretty clear what we need to do in the fn main()

The compiler gives us great information on what we should do literally showing us what we can do to make the code compile. So let's try it.

move_semantics6.rs solution

There we have it our solution is to again swap the & symbol's position to match that of the function's signature to make sure that we are borrowing and taking ownership as the function expects. With that we get our print out:

Rust's move semantics are important for understanding memory management and ownership in the language. By leveraging references, borrowing, and ownership, Rust ensures memory safety and eliminates many common programming errors like null pointer dereferences and dangling references.

In this blog post, we explored three exercises related to move semantics. We refactored code, re-ordered lines, and adjusted ownership to solve the problems. Through these exercises, we gained a better understanding of how move semantics work in Rust and how to manipulate ownership and references effectively.

Move semantics play a crucial role in Rust's design philosophy, enabling high-performance and safe code without sacrificing expressiveness. By embracing move semantics and mastering the intricacies of ownership, borrowing, and references, Rust developers can write robust and efficient code.

Remember, practice is key to mastering move semantics and other advanced features of Rust. Keep exploring, experimenting, and building projects to deepen your understanding and become a proficient Rust programmer. Happy coding!

IMAGES

  1. 10 Easy Steps: How to Write Acknowledgement for Assignment 2024

    assignment requires that borrow lasts for 'a

  2. How to Complete Your Assignment Quickly

    assignment requires that borrow lasts for 'a

  3. How To Make An Assignment

    assignment requires that borrow lasts for 'a

  4. 5 Tips to Write an Awesome Assignment in 2023

    assignment requires that borrow lasts for 'a

  5. How to Write an Assignment: Step by Step Guide

    assignment requires that borrow lasts for 'a

  6. Solved Note: This assignment requires that your final

    assignment requires that borrow lasts for 'a

VIDEO

  1. Assignment

  2. best hairstyles for guys in 2024

  3. Let me borrow your quilt and use it to make your life light and active without being afraid of the

  4. Assignment

  5. English new book class 6, unit 9, Lesson 9.6 page 72-74(Politeness)

COMMENTS

  1. Argument requires that borrow lasts for `'a`

    It's probably not what you want. Rust references are not generally usable for referencing objects longer than for a duration of a single function call. They are unsuitable for what other languages mean by "storing by reference". They are for borrowing objects temporarily and preventing them from being moved. Do not use references inside structs.

  2. Rust: argument requires that borrow lasts for `'static`

    1 Answer. The chunks are of the type & [char], with the same lifetime as the string. Simply cloning here won't work, as at compile time it doesn't know the size of the char array. To get a owned copy of a unknown sized array, you must convert it to a Vec, which is done easily with the to_vec () function. let mut threads = vec![];

  3. Argument requires that ... is borrowed for `'static`

    So if 'enc gets to be 'static, in the latter case, this would only happen if the &mut borrow of self were that long, hence dodging the issue, whereas for the FnMut… ~ for<'any> &'any mut Self : FnOnce… case, the &mut self borrow could remain independently small, hence making the implementation impossible.

  4. Improve borrow error when assigning borrowed local value to ...

    (Note the change from mut p: &mut i32 to p: &mut &mut i32, as it's the only change aside from updating deref counts to match). Going back to the phrasing: a &mut i32 owned by the caller's scope. I would argue this implies the reference (not the underlying data) is owned by the caller. Which, isn't really the case, as it's actually pass-by-move-ing the reference, so the caller doesn't actually ...

  5. Static

    See the following example for a display of each method: // Make a constant with `'static` lifetime. // lifetime is coerced to that of the input argument. &NUM. // Make a `string` literal and print it: let static_string = "I'm in read-only memory"; println!("static_string: {}", static_string);

  6. Use borrowed types for arguments

    In this way, the function will accept more input types. This is not limited to slice-able or fat pointer types. In fact, you should always prefer using the borrowed type over borrowing the owned type . Such as &str over &String, & [T] over &Vec<T>, or &T over &Box<T>. Using borrowed types you can avoid layers of indirection for those instances ...

  7. References and Borrowing

    The Rules. Here are the rules for borrowing in Rust: First, any borrow must last for a scope no greater than that of the owner. Second, you may have one or the other of these two kinds of borrows, but not both at the same time: one or more references ( &T) to a resource, exactly one mutable reference ( &mut T ).

  8. argument requires that [...] is borrowed for [...] #101120

    Hi 👋. I was instructed to create an issue here at a users.rust-lang.org post. Given the following code: Rust Playground (the "Run" button seems to be broken ATM though) use std::sync::Arc; use toki...

  9. A Lightweight Formalism for Reference Lifetimes and Borrowing in Rust

    This program is not considered type and borrow safe because, in the assignment ... In the last statement, the box moves from $\mathtt {x}$ to $\mathtt {y} ... Here, V-Borrow conservatively requires the borrowed reference matches one of the borrowed locations, but not all (i.e., since this is physically impossible). We extend this notion to ...

  10. Argument requires that `a` is borrowed for `'static`

    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. ... Activity; Argument requires that ... is borrowed for `'static` help. 10: 13670: January 26, 2022 ... help. 15: 1487: December 6, 2022 Mutable borrow in a loop. help. 5: 1291: March 11, 2023 Home ...

  11. Iteration

    DoubleEndedIterator inherits from Iterator (meaning all DoubleEndedIterator are Iterators) and requires one new method: next_back. It has the exact same signature as next, but it's supposed to yield elements from the other end. The semantics of DoubleEndedIterator are super convenient for us: the iterator becomes a deque.

  12. "lifetime of borrowed pointer outlives lifetime of captured variable

    Nominating for discussion at NLL meeting, just in terms of determining how this should be tagged to ensure that we keep it open until #![feature(nll)] is out of migration and totally stabilized.. Also, it needs tests that encode the current semantics under all three revisions: borrowck=ast, borrowck=migrate, and borrowck=mir.

  13. how to fix 'creates a temporary which is freed while still in use'?

    2. Use Option<Result> and drop the &. Since Cacher needs to come up with the result and store it for future invocations, it needs to own it. A reference can refer to the data that someone else owns, and there is no one else to own Result. (This is the kind of code that C or C++ would happily allow you to write, and which would result in a crash ...

  14. 07 Rustlings Move Semantics Part 2

    Reference and borrowing. Move Semantics Part 2. We've already covered the first three exercises of Move Semanticsin part one, in this episode we tackle the next three. Let's get started! move_semantics4.rs. // move_semantics4.rs// Refactor this code so that instead of passing `vec0` into the `fill_vec` function,// the Vector gets created in the ...

  15. Temporary value is freed at the end of this statement

    This one seems convoluted at first, but remember that String and &str are different beasts.. String can live and be used on its own, but &str is just a reference to part of String.So, &str can live as long as referenced String lives. Lets see how it should work on return signatures. let title_text = title .text() .trim(); // ^ ^ ^ // Node String <- &str

  16. Creates a temporary which is freed while still in use Again :)

    Hmm whats going wrong? Is it why i borrow the variable that i move to location and why location_test, its an cool String or i think an str? Thx. Edit: arrrrrgs he mean the &adress???? But why ???? Edit: Ahhh he means the variable process, or???? Why process is freed after use???

  17. String and memory allocation

    It's a matter of ownership/borrowing, not allocation. Heap allocation is just an implementation detail of the String and the borrow checker doesn't care about it. You can replace the String type with ArrayString<[u8; 1024]>, which never allocates heap memory, and would observes identical behavior.. The type String owns the type str(via heap allocated buffer, but it's not important) and the ...