rust question: temporary value dropped while borrowed [duplicate] - rust

This question already has answers here:
Why can I return a reference to a local literal but not a variable?
(1 answer)
why does rust allow a local struct reference had static lifetime? [duplicate]
(1 answer)
Closed 18 days ago.
i have code below,
enum List<'a> {
Cons(i32, Box<&'a List<'a>>),
Nil,
};
let list= Cons(10, Box::new(&Nil));
let lista = Cons(5, Box::new(&Cons(10, Box::new(&Nil))));
match lista {
Cons(x,_) => println!("{}",x),
Nil => ()
}
after running the code, the compiler says
21 | let lista = Cons(5, Box::new(&Cons(10, Box::new(&Nil))));
| ^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
i can understand 'Cons(10, Box::new(&Nil))' is freed, so it goes wrong. but when i replace list with lista after match, eg:
match list {
Cons(x,_) => println!("{}",x),
Nil => ()
}
i think Nil is also a temporary value and dropped at the end of statement, but it runs well, what's the difference between list and lista?

Its because of rvaue static promotion. Enum variants with all const values are constexpr, and therefore are promoted to a 'static lifetime.
For example:
enum TestEnum {
Dynamic(String),
NoValue,
ConstStr(&'static str)
}
fn main () {
use TestEnum::*;
println!("NoValue: {:?}", &NoValue as *const TestEnum);
println!("NoValue: {:?}", &NoValue as *const TestEnum);
println!("ConstStr: {:?}", &ConstStr("const-str") as *const TestEnum);
println!("ConstStr: {:?}", &ConstStr("const-str") as *const TestEnum);
println!("Dynamic: {:?}", &Dynamic(String::from("dynamic")) as *const TestEnum);
println!("Dynamic: {:?}", &Dynamic(String::from("dynamic")) as *const TestEnum);
}
outputs:
NoValue: 0x7ff6a92cf380
NoValue: 0x7ff6a92cf380
ConstStr: 0x7ff6a92cf3e8
ConstStr: 0x7ff6a92cf3e8
Dynamic: 0xd56df7f578
Dynamic: 0xd56df7f5f8
Only the Dynamic variant is stored in a new address on creation. You can also see this with explicit lifetimes:
// Compiles fine
let static_vaue : &'static TestEnum = &ConstStr("const-str");
// Does not compile
let dynamic_value : &'static TestEnum = &Dynamic(String::from("const-str"));
So the reason your code works when using Nil is because the Nil value being referenced does not get dropped after the statement as it is living in 'static memory, so it will never be dropped.

Related

error[E0716]: temporary value dropped while borrowed (simple struct example) [duplicate]

This question already has answers here:
Why can I return a reference to a local literal but not a variable?
(1 answer)
Why is it legal to borrow a temporary?
(3 answers)
Closed last month.
Currently, I'm learning the Lifetimes topic in Rust.
And I kind of get it, but recently I've came across this simple code:
#[derive(Debug)]
struct Test {
val: i32
}
fn main() {
let a:&Test;
let b = 32;
a = &Test {
val: b
};
println!("{:?}", a.val);
}
But it won't compile, with the respective error provided:
error[E0716]: temporary value dropped while borrowed
I know how to fix it & I do understand, that a temporary was created at line 10 and will be destroyed at the end of the statement at line 12.
(also the compiler itself states the same)
What I don't understand, it's why the following code actually will compile?
#[derive(Debug)]
struct Test {
val: i32
}
fn main() {
let a:&Test;
let b = 32;
a = &Test {
val: 5
};
println!("{:?}", a.val);
}
Only struct initialization values has been changed.
The val was b and now it's 5.
My guess is that the value of b, which is now owned by val field of our struct instance will be dropped at the end of the statement.
And therefore we will end up with the dangling reference.
Oh ... and one more interesting example.
#[derive(Debug)]
struct Test {
val: i32
}
fn main() {
let a:&Test;
let b = 32;
let a = &Test {
val: b
};
println!("{:?}", a.val);
}
This will also successfully compile.
It's the same as the first example, but we shadowing the a variable with let.
So, my fellow rustaceans ... why it's working this way?
Case 1
a = &Test { val: b };
println!("{a:?}");
Let's call this one the base case. No "magic" happens, the temporary value is dropped at the end of the statement, and the compiler complains "temporary value dropped while borrowed".
Case 2
a = &Test { val: 5 };
println!("{a:?}");
The key difference here is we now have a constant expression due to the literal 5, so it is eligible for constant promotion:
Promotion of a value expression to a 'static slot occurs when the expression could be written in a constant and borrowed, and that borrow could be dereferenced where the expression was originally written, without changing the runtime behavior.
Case 3
let a = &Test { val: b };
println!("{a:?}");
Finally, here we have a let statement, which is eligible for temporary lifetime extension. According to the reference,
The temporary scopes for expressions in let statements are sometimes extended to the scope of the block containing the let statement
but also,
The exact rules for temporary lifetime extension are subject to change. This is describing the current behavior only
Evidently, temporary lifetime extension for let statements doesn't occur when the let statement is separated from its initializer.

RefCell cannot return string slice from String; temporary value problem

im trying to return a &str slice from a String which is behind a an Rc<RefCell<>>
use std::rc::Rc;
use std::cell::{
RefCell,
Ref
};
struct Data {
data: Rc<RefCell<String>>
}
impl Data {
fn set(
&mut self,
data: String
) {
*self.data.borrow_mut() = data;
}
// TODO: fix here
fn get(&self) -> &str {
self.data.borrow().as_str()
}
}
fn main() {
let mut data = Data {
data: Rc::new(RefCell::new(String::new()))
};
data.set(String::from("how can i solve this?"));
let d = data.get();
println!("{}", d);
}
here's the compilation error
error[E0515]: cannot return reference to temporary value
--> questions/refcell-data-inspection/src/main.rs:21:9
|
21 | self.data.borrow().as_str()
| ------------------^^^^^^^^^
| |
| returns a reference to data owned by the current function
| temporary value created here
well, i understand its a temporary value crated because borrow returns a guard, which will go out of scope.
how can i fix this in order to return a &str slice from that String?
Well, as you said yourself "borrow guard" will be out of scope at the one of a function, so you cannot do that. Think of it that way. If you managed to return a reference into String, how would RefCell know that this reference exists and that it cannot give for example a mutable reference.
Only thing that you can do is to return that guard. This would mean, that you must change function's signature, but it will be as usable as a &str would be. So try this:
fn get(&self) -> Ref<'_, String> {
self.data.borrow()
}
EDIT. You might want to also look at that question: How to borrow the T from a RefCell<T> as a reference?

Taking ownership of a &String without copying

Context
Link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9a9ffa99023735f4fbedec09e1c7ac55
Here's a contrived repro of what I'm running into
fn main() {
let mut s = String::from("Hello World");
example(&mut s);
}
fn example(s: &mut str) -> Option<String> {
other_func(Some(s.to_owned()))
// other random mutable stuff happens
}
fn other_func(s: Option<String>) {
match s {
Some(ref s) => other_func2(*s),
None => panic!()
}
}
fn other_func2(s: String) {
println!("{}", &s)
}
and the error
Compiling playground v0.0.1 (/playground)
error[E0507]: cannot move out of `*s` which is behind a shared reference
--> src/main.rs:12:36
|
12 | Some(ref s) => other_func2(*s),
| ^^ move occurs because `*s` has type `String`, which does not implement the `Copy` trait
Question
In the following code, why can't I deference the &String without having to do some sort of clone/copy? i.e. this doesn't work
fn other_func(s: Option<String>) {
match s {
Some(ref s) => other_func2(*s),
None => panic!()
}
}
but it works if I replace *s with s.to_owned()/s.to_string()/s.clone()
As an aside, I understand this can probably be solved by refactoring to use &str, but I'm specifically interested in turning &String -> String
Why would the compiler allow you to?
s is &String. And you cannot get a String from a &String without cloning. That's obvious.
And the fact that it was created from an owned String? The compiler doesn't care, and it is right. This is not different from the following code:
let s: String = ...;
let r: &String = ...;
let s2: String = *r; // Error
Which is in turn not different from the following code, for instance, as far as the compiler is concerned:
let r: &String = ...;
let s: String = *s;
And we no longer have an owned string at the beginning. In general, the compiler doesn't track data flow. And rightfully so - when it type-checks the move it doesn't even can confirm that this reference isn't aliased. Or that the owned value is not used anymore. References are just references, they give you no right to drop the value.
Changing that will not be feasible in the general case (for example, the compiler will have to track data flow across function calls), and will require some form of manual annotation to say "this value is mine". And you already have such annotation - use an owned value, String, instead of &String: this is exactly what it's about.

How to return a reference to a value owned by a Vec within a Rc<RefCell<_>>? [duplicate]

This question already has answers here:
Obtain a reference from a RefCell<Option<Rc<T>>> in Rust
(1 answer)
How do I borrow a RefCell<HashMap>, find a key, and return a reference to the result? [duplicate]
(1 answer)
How do I return a reference to something inside a RefCell without breaking encapsulation?
(3 answers)
Closed 12 months ago.
Assume following code
pub struct Universe {
components: Rc<RefCell<Vec<Component>>>,
active_component: Rc<RefCell<Option<usize>>>,
}
I would like to introduce a convenience method that returns a mutable reference to the active component, e.g.
fn get_active_component(&mut self) -> Option<&mut Component> {
if let Some(active_component_idx) = self.active_component.borrow().as_ref() {
let i = *active_component_idx;
return self.components.borrow_mut().get_mut(i);
}
Option::None
}
which results in error
145 | return self.components.borrow_mut().get_mut(i);
| ----------------------------^^^^^^^^^^^
| |
| returns a reference to data owned by the current function
| temporary value created here
I do understand the error. The borrow_mut() creates a temporary variable which goes out of scope after the function returns. But I have absolutely no idea how you would realize such a method in rust apart from always inlining the code.
The standard way would be to mimic what RefCell does -- return a proxy struct wrapping the RefMut from .borrow_mut() and containing the vector index, implementing Deref and DerefMut.
pub struct ComponentHandle<'a> {
vecref: RefMut<'a, Vec<Component>>,
index: usize,
}
impl Deref for ComponentHandle<'_> {
type Target = Component;
fn deref(&self) -> &Component {
// SAFETY: We already verified the index is valid, RefCell won't
// dispense another mutable reference while we hold the RefMut, and we
// don't modify the vector's length, so we know this index is valid.
unsafe { self.vecref.get_unchecked(self.index) }
}
}
impl DerefMut for ComponentHandle<'_> {
fn deref_mut(&mut self) -> &mut Component {
// SAFETY: We already verified the index is valid, RefCell won't
// dispense another mutable reference while we hold the RefMut, and we
// don't modify the vector's length, so we know this index is valid.
unsafe { self.vecref.get_unchecked_mut(self.index) }
}
}
impl Universe {
fn get_active_component(&mut self) -> Option<ComponentHandle<'_>> {
if let Some(active_component_idx) = self.active_component.borrow().as_ref() {
let vecref = self.components.borrow_mut();
let index = *active_component_idx;
if index < vecref.len() {
return Some(ComponentHandle { vecref, index });
}
}
None
}
}
Alternatively, this function could accept a closure to invoke, passing it the bare reference. This is simpler to code, though less idiomatic:
fn get_active_component<F>(&mut self, f: F)
where F: FnOnce(Option<&mut Component>)
{
if let Some(active_component_idx) = self.active_component.borrow().as_ref() {
let i = *active_component_idx;
f(self.components.borrow_mut().get_mut(i));
} else {
f(None);
}
}

captured variable does not outlive the enclosing closure [duplicate]

This question already has an answer here:
Lifetime troubles sharing references between threads
(1 answer)
Closed 7 years ago.
I have a threading operation in Rust which requires the use of a variable passed as an argument to the function the threads are spawned within, however I am seeing the following compile time error:
Compiling Test v0.0.1 (file:///Users/clarkj84/Desktop/RustTest)
main.rs:9:22: 9:35 error: captured variable `test` does not outlive the enclosing closure
main.rs:9 let handle = thread::spawn(move || {
^~~~~~~~~~~~~
main.rs:7:20: 16:2 note: captured variable is valid for the anonymous lifetime #1 defined on the block at 7:19
main.rs:7 fn test(test: &str){
main.rs:8 for _x in 0..2 {
main.rs:9 let handle = thread::spawn(move || {
main.rs:10 for _y in 0..2 {
main.rs:11 println!("{}", test);
main.rs:12 }
...
note: closure is valid for the static lifetime
error: aborting due to previous error
Could not compile `Test`.
To learn more, run the command again with --verbose.
This is my code implementation:
use std::thread;
fn main() {
test("test");
}
fn test(test: &str){
for _x in 0..2 {
let handle = thread::spawn(move || {
for _y in 0..2 {
println!("{}", test);
}
});
}
}
Checkout #Shepmaster's answer for an explanation. There are two solutions to your problem depending on your use-case.
If your use case is just string literals (of type &'static str) and not arbitrary strings that are created during the program's execution, you can modify your function's signature to
fn test(test: &'static str)
This will allow you to pass any string literal to this function and move the string literal to another thread.
If on the other hand you want to create new strings by e.g. taking them from program arguments or environment variables, then you can create one String object per thread and pass that.
use std::thread;
use std::env;
fn main() {
test("test");
test(std::env::var("HOME").unwrap()); // pass the current home-directory
}
fn test(test: &str) {
for _x in 0..2 {
let string_object = test.to_string();
let handle = thread::spawn(move || {
for _y in 0..2 {
println!("{}", string_object);
}
});
}
}

Resources