How do lifetimes in Rust work for a function? - rust

In the definition of lifetime_things, the lifetime of 'b is longer than 'a, but actually when I call this function, x1 is longer than y1, but this can compile successfully:
//here you could see 'b:'a means, the lifetime of b should be larger than a,
fn lifetime_things<'a,'b:'a>(x:&'a String, y:&'b String) ->&'a String {
if x.len() > y.len() {
&x
} else {
&y
}
}
fn main() {
let z: i32;
let x = String::from("1");
let x1=&x;
{
let y = String::from("2");
let y1=&y;
println!("{}",lifetime_things(x1,y1));
}
}
But here you could see the lifetime of x1 should be larger than y1 so why can this compile successfully as well?

'b: 'a means 'b must outlive 'a, which in turn means whenever 'a is live, so must 'b. Crucially, this is a larger than or equals relationship. Also notice that lifetimes don't work like types. When a function takes an 'v reference, any lifetime ('w) will be accepted as long as 'w: 'v.
So, where is 'a live? In the body of the function and whenever the returned string reference is used after the call. We can visualize it like this:
fn lifetime_things<'a,'b:'a>(x:&'a String, y:&'b String) ->&'a String {
if x.len() > y.len() { // <-+
&x // |
} else { // |
&y // +----+
} // | |
} // <-+ |
// |
fn main() { // +-- 'a
let z: i32; // |
let x = String::from("1"); // |
let x1=&x; // |
{ // |
let y = String::from("2"); // |
let y1=&y; // |
println!("{}",lifetime_things(x1,y1)); // <------+
}
}
Notice, that since the value returned by lifetime_things is only printed, 'a is only live on the line println!("{}",lifetime_things(x1,y1)); (exclusing lifetime_thingss body).
So, to call lifetime_things we simply need the both arguments to at least be live at the call. This holds for both x1 and y1, so it all checks out.

Related

Can I specify a closure which captures nothing?

While closures are powerful, they can lead to bugs if used carelessly. An example is a closure that modifies its captured variables when this is not wanted. Sometimes we only need an anonymous function that has no free variables. That is, it captures nothing. Can I specify a closure as capturing nothing and get this checked by the compiler? Something like none |...| {...} looks ideal. I know I can define a function in current scope but it's not as elegant as a variable containing an anonymous function.
Only non capturing closures can coerce to function pointers. You can use this fact to check that the closure is non-capturing by attempting to coerce it to a function pointer:
let closure = || {};
let _: fn() = closure;
You can wrap it in a macro to make this convenient:
macro_rules! enforce_non_capturing {
// This macro does not allow specifying the return type (`|| -> Ret {}`),
// but it can be adjusted to allow that.
// It also does not allow patterns as parameter names, but allowing that
// is harder.
(
| $( $param:ident $( : $param_ty:ty )? ),* $(,)? | $body:expr
) => {{
let closure = | $( $param $( : $param_ty )?, )* | $body;
// We want to generate `fn(_, _, ...) -> _` with underscores as the number of parameters.
// We use a dummy repetition to achieve that.
let _: fn( $( enforce_non_capturing!(#replace_with_underscore $param ), )* ) -> _ = closure;
closure
}};
// `||` is recognized as one token instead of two, so we need another arm.
( || $body:expr ) => { enforce_non_capturing!(| | $body) };
(#replace_with_underscore $param:ident) => { _ };
}
fn main() {
// Compiles.
let closure = enforce_non_capturing!(|| {});
closure();
let a = 0;
// Doesn't compile.
// let closure = enforce_non_capturing!(|| a);
// closure();
}
Playground.
Maybe the constraint could not be expressed where the closure/function is provided, but where it is expected.
In this example, fnct2() cannot receive a closure.
fn fnct1(mut f: impl FnMut(i32) -> i32) {
println!("{}", f(10));
}
fn fnct2(f: fn(i32) -> i32) {
println!("{}", f(20));
}
fn main() {
let mut x = 0;
fnct1(|n| {
x += 1;
n + x
});
fnct2(|n| {
// x += 2; // ERROR: expected fn pointer, found closure
// n + x
n + 2
});
println!("x={}", x);
}
/*
11
22
x=1
*/
While this is most certainly not a 'best practice', you can store the closure in a function pointer. Function pointers can only hold non-capturing closures.
This works:
fn main() {
let c: fn() = || {
println!("Hello world!");
};
c();
}
Hello world!
While this doesn't:
fn main() {
let mut a = 10;
let c: fn() = || {
a += 1;
};
}
error[E0308]: mismatched types
--> src/main.rs:3:19
|
3 | let c: fn() = || {
| ____________----___^
| | |
| | expected due to this
4 | | a += 1;
5 | | };
| |_____^ expected fn pointer, found closure
|
= note: expected fn pointer `fn()`
found closure `[closure#src/main.rs:3:19: 3:21]`
note: closures can only be coerced to `fn` types if they do not capture any variables
--> src/main.rs:4:9
|
4 | a += 1;
| ^ `a` captured here

Why does Rust not automatically move when necessary?

The following program compiles without issue:
#[tokio::main]
async fn main() {
async fn g(x: String) {}
let f = || {
let y: String = String::from("a").clone();
return async {
println!("{}", &y);
return g(y).await;
}};
}
However, if the line "return g(y).await;" is removed, it will fail with the following:
error[E0373]: async block may outlive the current function, but it borrows `y`, which is owned by the current function
--> src/main.rs:35:22
|
35 | return async {
| ______________________^
36 | | println!("{}", &y);
| | - `y` is borrowed here
37 | | // return g(y).await;
38 | | }};
| |_____^ may outlive borrowed value `y`
|
note: async block is returned here
--> src/main.rs:35:16
|
35 | return async {
| ________________^
36 | | println!("{}", &y);
37 | | // return g(y).await;
38 | | }};
| |_____^
help: to force the async block to take ownership of `y` (and any other referenced variables), use the `move` keyword
|
35 | return async move {
| ++++
Why does the same error not appear in the original code?
Rust does the minimum amount of work necessary to get your closure to work.
let f = || {
let y: String = String::from("a").clone();
return async {
println!("{}", &y);
}
};
Here, the inner closure requires y by reference. So Rust is going to turn it into, essentially, a struct with a &String. Removing the async stuff for simplicity, it would turn this
let f = || {
let y: String = String::from("a").clone();
|| {
println!("{}", &y);
}
};
into, effectively, this
struct MyCustomClosure<'a> { y: &'a String };
impl<'a> FnOnce for MyCustomClosure<'a> {
fn call_once(self) {
println!("{}", self.y)
}
}
// (Same impl for FnMut and Fn ...)
let f = || {
let y: String = String::from("a").clone();
return MyCustomClosure { y: &y }
};
Now, sometime way later on in the compilation process, Rust realizes that the lifetime 'a for MyCustomClosure doesn't line up with the lifetime for the enclosing function, and it complains. But by this point it's already committed to using a reference here and it's not smart enough to go back and try a different closure type. It's two different stages of compilation that don't talk to each other directly.
This on the other hand
let f = || {
let y: String = String::from("a").clone();
|| { y }
};
This, on the other hand, very clearly requires a move. We're passing ownership inside the closure, so we get a closure that only implements FnOnce and that takes the y by value. Essentially we get
struct MyCustomClosure2 { y: String };
impl FnOnce for MyCustomClosure2 {
fn call_once(self) -> String {
self.y
}
}
// No FnMut or Fn this time, since we need to pass ownership of a value.
Now there's no lifetime argument 'a to cause conflicts down the road. There's just a simple struct and it all works out.
As the error message indicates, if your intent is to get an FnOnce which returns the string by moving, you can prefix your closure with the move keyword.
let f = || {
let y: String = String::from("a").clone();
return async move {
println!("{}", &y);
}
};

rust E0597: borrowed value does not live lnog enough

I am trying to rewrite an algorithm from javascript to rust. In the following code, I get borrowed value does not live long enough error at line number 17.
[dependencies]
scraper = "0.11.0"
use std::fs;
fn get_html(fname: &str) -> String {
fs::read_to_string(fname).expect("Something went wrong reading the file")
}
pub mod diff_html {
use scraper::{element_ref::ElementRef, Html};
pub struct DiffNode<'a> {
node_ref: ElementRef<'a>,
}
impl<'a> DiffNode<'a> {
fn from_html(html: &str) -> Self {
let doc = Self::get_doc(&html);
let root_element = doc.root_element().to_owned();
let diffn = Self {
node_ref: root_element,
};
diffn
}
fn get_doc(html: &str) -> Html {
Html::parse_document(html).to_owned()
}
}
pub fn diff<'a>(html1: &str, _html2: &str) -> DiffNode<'a> {
let diff1 = DiffNode::from_html(&html1);
diff1
}
}
fn main() {
//read strins
let filename1: &str = "test/test1.html";
let filename2: &str = "test/test2.html";
let html1: &str = &get_html(filename1);
let html2: &str = &get_html(filename2);
let diff1 = diff_html::diff(html1, html2);
//write html
//fs::write("test_outs/testx.html", html1).expect("unable to write file");
//written output file.
}
warning: unused variable: `diff1`
--> src\main.rs:43:9
|
43 | let diff1 = diff_html::diff(html1, html2);
| ^^^^^ help: if this is intentional, prefix it with an underscore: `_diff1`
|
= note: `#[warn(unused_variables)]` on by default
error[E0597]: `doc` does not live long enough
--> src\main.rs:17:32
|
14 | impl<'a> DiffNode<'a> {
| -- lifetime `'a` defined here
...
17 | let root_element = doc.root_element().to_owned();
| ^^^--------------------------
| |
| borrowed value does not live long enough
| assignment requires that `doc` is borrowed for `'a`
...
22 | }
| - `doc` dropped here while still borrowed
I want a detailed explanation/solution if possible.
root_element which is actually an ElementRef has reference to objects inside doc, not the actual owned object. The object doc here is created in from_html function and therefore owned by the function. Because doc is not returned, it is dropped / deleted from memory at the end of from_html function block.
ElementRef needs doc, the thing it is referencing to, to be alive when it is returned from the memory.
pub mod diff_html {
use scraper::{element_ref::ElementRef, Html};
pub struct DiffNode<'a> {
node_ref: ElementRef<'a>,
}
impl<'a> DiffNode<'a> {
fn from_html(html: &'a scraper::html::Html) -> Self {
Self {
node_ref: html.root_element(),
}
}
}
pub fn diff<'a>(html1_string: &str, _html2_string: &str) {
let html1 = Html::parse_document(&html1_string);
let diff1 = DiffNode::from_html(&html1);
// do things here
// at the end of the function, diff1 and html1 is dropped together
// this way the compiler doesn't yell at you
}
}
More or less you need to do something like this with diff function to let the HTML and ElementRef's lifetime to be the same.
This behavior is actually Rust's feature to guard values in memory so that it doesn't leak or reference not referencing the wrong memory address.
Also if you want to feel like operating detachable objects and play with reference (like java, javascript, golang) I suggest reading this https://doc.rust-lang.org/book/ch15-05-interior-mutability.html

How does ownership and borrowing work when making slices?

Here are two code snippets, but they show a different behavior and I couldn't understand what's going on in there. Their main function is identical. If the borrowing and ownership concept applies to one code (i.e to code 2) why not to another code i.e (code 1)?
code 1:
This code compiles with no errors and prompt the result.
fn main() {
let mut s = String::from("Hello world");
let result = first_word(&s);
s.clear();
println!("result:{:#?}", result);
}
fn first_word(s: &String) -> usize {
let s = s.as_bytes();
//println!("{:?}",s);
for (i, &item) in s.iter().enumerate() {
if item == 32 {
return i;
}
}
s.len()
}
Code 1 Output :
Finished dev [unoptimized + debuginfo] target(s) in 0.28s
Running `target/debug/rust_Slices`
result:5
Code 2:
This code won't compile and gives an error.
fn main() {
let mut s = String::from("Hello world");
let result = first_word(&s);
s.clear();
println!("{:#?}", result);
}
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
Code 2 Output:
cannot borrow `s` as mutable because it is also borrowed as immutable
--> src/main.rs:4:4
|
3 | let result = first_word(&s);
| -- immutable borrow occurs here
4 | s.clear();
| ^^^^^^^^^ mutable borrow occurs here
5 | println!("{:#?}",result);
| ------ immutable borrow later used here
Let's decompose:
// Let's build a string, which basically is a growable array of chars
let mut s = String::from("Hello world");
// now make result a slice over that string, that is a reference
// to a part of the underlying chars
let result = first_word(&s);
// now let's remove the content of the string (which of course removes
// what the slice was referring to)
s.clear();
// and let's... print it ?
println!("{:#?}", result);
Hopefully the borrow checker prevents you from doing this with this exact error:
cannot borrow s as mutable because it is also borrowed as immutable
And if you've understood this, the solution should be obvious: don't make result a window over another string but a string by itself, having its own content: change the second line to
let result = first_word(&s).to_string();
Now you can clear the source string and keep the first word. Of course to_string() isn't a costless operation so you might want to try keep the source string around in real applications.
The key thing here is lifetimes. By default lifetimes argument for functions with one input reference and output reference are the same (liftime elision). So compiler implicitly changes the code following way:
fn first_word<'a>(s: &'a String) -> &'a str { // note 'a here
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
That means that the result borrows the input argument. You can explicitly make lifetimes different and eliminate error in the main but in this case first_word will not compile:
fn first_word1<'a, 'b>(s: &'a String) -> &'b str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
--> src/main.rs:7:21
|
7 | return &s[0..i];
| ^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime 'a as defined on the function body

Why can borrowed string literal outlive its owner by faking a lifetime?

I understand that a borrow cannot outlive the existence of the thing it points to, to eradicate the dangling pointers.
A borrow or an alias can outlive the owner by faking the lifetimes:
fn main() {
let e;
let first = "abcd";
{
let second = "defgh";
e = longest(first, second);
}
println!("{}", e);
}
fn longest<'a>(first: &'a str, second: &'a str) -> &'a str {
if first.len() > second.len() {
first
} else {
second
}
}
Result:
defgh
In the above example, the variable e has a longer lifetime than the second variable and clearly the first & second variables lifetimes are different.
When e is initialized with longest(first, second) it gets the second variable whose lifetime to function call is faked as it is equal to first but it is confined to the block and it is assigned to e which will outlive the second. Why is this OK?
This is due to the fact that both of these have the 'static lifetime.
Here's an example that doesn't work because the str here does not live for the life of the program like a &'static str does.
The only change is the following line: let second = String::from("defgh"); and the next line where it is passed to the longest function.
fn main() {
let e;
let first = "abcd";
{
let second = String::from("defgh");
e = longest(first, &second);
}
println!("{}", e);
}
fn longest<'a>(first: &'a str, second: &'a str) -> &'a str {
if first.len() > second.len() {
first
} else {
second
}
}
Here's the error:
error[E0597]: `second` does not live long enough
--> src/main.rs:6:28
|
6 | e = longest(first, &second);
| ^^^^^^^ borrowed value does not live long enough
7 | }
| - `second` dropped here while still borrowed
8 | println!("{}", e);
| - borrow later used here
More information can be found in Static - Rust By Example

Resources