Skip to content

Commit 5adf590

Browse files
committed
feat: add exercise involving lifetime elision
1 parent ea504e6 commit 5adf590

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed

exercises/lifetimes/lifetimes4.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// lifetimes4.rs
2+
//
3+
// Sometimes, we have structs which hold on to data temporarily. A use-case of
4+
// this could be a routing component which accepts a connection and returns it to
5+
// another recipient. To avoid copying the data, we just accept a reference with
6+
// lifetime and return this reference later.
7+
//
8+
// In the example below, we create a `Router` instance in a limited scope. It
9+
// accepts a connection reference created in the enclosing scope and returns it.
10+
// In theory, this should be possible given that the connection reference outlives
11+
// the scope from which it is returned. However the borrow checker does not
12+
// seem to understand it. What can we do about that?
13+
//
14+
// Execute `rustlings hint lifetimes4` or use the `hint` watch subcommand for a
15+
// hint.
16+
17+
// I AM NOT DONE
18+
19+
struct Router<'a> {
20+
connection: Option<&'a u64>,
21+
}
22+
23+
impl<'a> Router<'a> {
24+
fn new() -> Self {
25+
Self { connection: None }
26+
}
27+
28+
fn accept_connection(&mut self, connection: &'a u64) {
29+
self.connection = Some(connection);
30+
}
31+
32+
fn return_connection(&mut self) -> Option<&u64> {
33+
self.connection.take()
34+
}
35+
}
36+
37+
fn main() {
38+
let connection = &123;
39+
40+
let returned_connection = {
41+
// Create router within scope.
42+
let mut router = Router::new();
43+
44+
// Accept connection which lives longer than the router.
45+
router.accept_connection(connection);
46+
47+
// Return connection which **should** live longer than the router.
48+
router.return_connection()
49+
};
50+
51+
if let Some(connection) = returned_connection {
52+
println!("The connection is {connection}");
53+
}
54+
}

info.toml

+19
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,25 @@ hint = """
876876
If you use a lifetime annotation in a struct's fields, where else does it need
877877
to be added?"""
878878

879+
[[exercises]]
880+
name = "lifetimes4"
881+
path = "exercises/lifetimes/lifetimes4.rs"
882+
mode = "compile"
883+
hint = """
884+
The compiler complains that the `Router` is dropped at the end of the smaller
885+
scope while still being borrowed. However, we never intended to return a borrow
886+
to the `Router`. What are we really returning from
887+
`Router::return_connection`?
888+
889+
The method `Router::return_connection` only takes `&mut self`
890+
and returns `Option<&u64>`. No explicit lifetimes are specified. What lifetime
891+
will the borrow checker assume for the `&u64` in the `Option`? You may want to
892+
re-read the chapter on lifetime elision:
893+
https://doc.rust-lang.org/reference/lifetime-elision.html
894+
895+
What lifetime do we really want the `Option<&u64>` to have? Can we make that
896+
explicit?"""
897+
879898
# TESTS
880899

881900
[[exercises]]

0 commit comments

Comments
 (0)