Skip to content
New issue

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

feat: add exercise involving lifetime elision #1719

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions exercises/lifetimes/lifetimes4.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// lifetimes4.rs
//
// Sometimes, we have structs which hold on to data temporarily. A use-case of
// this could be a routing component which accepts a connection and returns it to
// another recipient. To avoid copying the data, we just accept a reference with
// lifetime and return this reference later.
//
// In the example below, we create a `Router` instance in a limited scope. It
// accepts a connection reference created in the enclosing scope and returns it.
// In theory, this should be possible given that the connection reference outlives
// the scope from which it is returned. However the borrow checker does not
// seem to understand it. What can we do about that?
//
// Execute `rustlings hint lifetimes4` or use the `hint` watch subcommand for a
// hint.

// I AM NOT DONE

struct Router<'a> {
connection: Option<&'a u64>,
}

impl<'a> Router<'a> {
fn new() -> Self {
Self { connection: None }
}

fn accept_connection(&mut self, connection: &'a u64) {
self.connection = Some(connection);
}

fn return_connection(&mut self) -> Option<&u64> {
self.connection.take()
}
}

fn main() {
let connection = &123;

let returned_connection = {
// Create router within scope.
let mut router = Router::new();

// Accept connection which lives longer than the router.
router.accept_connection(connection);

// Return connection which **should** live longer than the router.
router.return_connection()
};

if let Some(connection) = returned_connection {
println!("The connection is {connection}");
}
}
19 changes: 19 additions & 0 deletions info.toml
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,25 @@ hint = """
If you use a lifetime annotation in a struct's fields, where else does it need
to be added?"""

[[exercises]]
name = "lifetimes4"
path = "exercises/lifetimes/lifetimes4.rs"
mode = "compile"
hint = """
The compiler complains that the `Router` is dropped at the end of the smaller
scope while still being borrowed. However, we never intended to return a borrow
to the `Router`. What are we really returning from
`Router::return_connection`?

The method `Router::return_connection` only takes `&mut self`
and returns `Option<&u64>`. No explicit lifetimes are specified. What lifetime
will the borrow checker assume for the `&u64` in the `Option`? You may want to
re-read the chapter on lifetime elision:
https://doc.rust-lang.org/reference/lifetime-elision.html

What lifetime do we really want the `Option<&u64>` to have? Can we make that
explicit?"""

# TESTS

[[exercises]]
Expand Down
Loading