Rebind an owned variable inside a FnMut closure - rust

I am unable to rebind an owned variable that I have moved into a FnMut() closure. I think the following MRE summarises the problem.
fn takes_closure(mut do_something: impl FnMut()) {
do_something()
}
fn main() {
let test_string = String::from("hello");
takes_closure(move || {
//let mut test_string = test_string; // 1
let mut test_string = test_string.clone(); // 2
test_string.push_str("!"); // 3
});
}
The target behaviour would be that test_string has a ! appended to it each time the closure is called. Please note that it is not possible to rebind test_string in main as mutable due to the context of the original problem. These are the following issues that I have found:
Is not possible because this is "moving" out of the closure
Is incorrect since now I am modifying a copy of the string and not the original
Only works with #2 since it requires test_string to be bound mutably.

You can just declare test_string as mutable before the closure, either by directly making the original mutable:
fn main() {
let mut test_string = String::from("hello");
takes_closure(move || {
test_string.push_str("!"); // 3
});
}
or if you want to retain immutability until just before the closure, rebinding the variable just outside of the closure
fn main() {
let test_string = String::from("hello");
takes_closure({
let mut test_string = test_string;
move || {
test_string.push_str("!"); // 3
}
});
}
If you want to modify the original test_string and observe the change after the closure ran you have to move just a mutable reference in:
fn main() {
let mut test_string = String::from("hello");
takes_closure({
let test_string = &mut test_string;
move || {
test_string.push_str("!"); // 3
}
});
dbg!(test_string); // outputs 'test_string = "hello!"'
}

Related

Why am I allowed to have multiple &mut refs in nested functions (Rust)?

I'm new to rust, and am wondering why the following code doesn't result in a:
cannot borrow val as mutable more than once at a time error. It seems like by the time I've reached the second_layer function, I should have three separate references to the same original val variable:
val_ref in the main function body
val_ref2 in the first_layer function body
val_ref3 in the second_layer function body
Any help would be appreciated!
fn first_layer(val_ref2: &mut String)
{
*val_ref2 = String::from("first_layer");
println!("{}", val_ref2);
second_layer(val_ref2);
}
fn second_layer(val_ref3: &mut String)
{
*val_ref3 = String::from("second_layer");
println!("{}", val_ref3);
}
fn main()
{
let mut val = String::from("asdf");
let val_ref: &mut String = &mut val;
first_layer(val_ref);
println!("{}", val_ref);
}
Thanks,
References in a function calling another function temporarily do not exist for the duration of the function call.
Identifiers do not automatically live on in functions called further down the stack. Just because one function calls another does not mean that the callee should have access to the caller's local variables. You would get a not found in this scope if you tried.
What you can (try to) do is create a closure to capture the caller's environment and then reuse one of the caller's variables. But you will find that the compiler complains if you break the borrowing rules.
fn first_layer(val_ref2: &mut String) {
// println!("{}", val); // Cannot use val from main!
*val_ref2 = String::from("first_layer");
println!("{}", val_ref2);
(|val_ref3: &mut String| {
*val_ref3 = String::from("second_layer");
println!("{}", val_ref3);
println!("{}", val_ref2); // This is not allowed
})(val_ref2);
}
fn main() {
let mut val = String::from("asdf");
let val_ref: &mut String = &mut val;
first_layer(val_ref);
println!("{}", val_ref);
}
Another way to think about it is with inlining. If you inline your function second_layer into first_layer you get:
fn first_layer(val_ref2: &mut String)
{
*val_ref2 = String::from("first_layer");
println!("{}", val_ref2);
*val_ref2 = String::from("second_layer");
println!("{}", val_ref2);
}
This is totally fine!
So then the code with the last two lines abstracted to its own function should also be totally fine.

What's the proper way to use variables defined in the main thread in a child thread in Rust?

I'm new to Rust and still reading the Rust book. Below is my program.
use clap::{App, Arg};
type GenericError = Box<dyn std::error::Error + Send + Sync + 'static>;
type GenericResult<T> = Result<T, GenericError>;
fn main() -> GenericResult<()> {
let matches = App::new("test")
.arg(Arg::new("latency")
.takes_value(true))
.get_matches();
let latency_present = matches.is_present("latency");
let latency = matches.value_of("latency").unwrap_or("1000:10,300:30");
let latency_pairs: Vec<&str> = latency.split(",").collect();
let checker = std::thread::spawn(move || -> GenericResult<()>{
loop {
if latency_present {
for (i, latency_pair) in latency_pairs.iter().enumerate() {
// let latency_pair: Vec<&str> = latency_pair.split(":").collect();
// let latency = latency_pair[0].parse::<f64>().unwrap();
}
}
}
});
checker.join().unwrap()?;
Ok(())
}
When I run it, it tells me this:
error[E0597]: `matches` does not live long enough
--> src\main.rs:14:19
|
14 | let latency = matches.value_of("latency").unwrap_or("1000:10,300:30");
| ^^^^^^^--------------------
| |
| borrowed value does not live long enough
| argument requires that `matches` is borrowed for `'static`
...
30 | }
| - `matches` dropped here while still borrowed
I don't quite understand the error messages here. But I guess it's because I use latency_pairs in the checker thread and latency_pairs could get dropped while checker is still executing. Is my understanding correct? How to fix the error? I tried for (i, latency_pair) in latency_pairs.clone().iter().enumerate() { in order to pass a cloned value for the thread, but it doesn't help.
latency_pairs holds references into latency which in turn references matches. Thus cloning latency_pairs just clones the references into latency and matches.
Your code would require that latency's type is &'static str but it's actually &'a str where 'a is bound to matches' lifetime.
You can call to_owned() on latency to get an owned value and split the string inside the closure or you can call to_owned() on each of the splits collected in latency_pairs and move that Vec<String> into the closure:
let latency_pairs: Vec<String> = latency.split(",").map(ToOwned::to_owned).collect();
let checker = std::thread::spawn(move || -> GenericResult<()>{
loop {
if latency_present {
for (i, latency_pair) in latency_pairs.iter().enumerate() {
// let latency_pair: Vec<&str> = latency_pair.split(":").collect();
// let latency = latency_pair[0].parse::<f64>().unwrap();
}
}
}
});
If you need to use latency_pairs outside of the closure, you can clone it before moving it into the closure:
let latency_pairs: Vec<String> = latency.split(",").map(ToOwned::to_owned).collect();
let latency_pairs_ = latency_pairs.clone();
let checker = std::thread::spawn(move || -> GenericResult<()>{
loop {
if latency_present {
for (i, latency_pair) in latency_pairs_.iter().enumerate() {
// let latency_pair: Vec<&str> = latency_pair.split(":").collect();
// let latency = latency_pair[0].parse::<f64>().unwrap();
}
}
}
});
println!("{:?}", latency_pairs);

How do I return local data with a recursive function in Rust?

I'm struggling to write a recursive algorithm in Rust. With the following code:
use std::collections::HashMap;
enum Error {
Bad,
ReallyBad,
}
fn expand_symbols<'a, T: AsRef<str>>(
symbols: &'a [T],
ops: &HashMap<String, String>,
user_ops: &'a HashMap<String, String>,
) -> std::result::Result<Vec<&'a str>, Error> {
if symbols.iter().all(|x| ops.get(x.as_ref()).is_some()) {
let symbols = symbols.iter().map(|x| x.as_ref()).collect();
return Ok(symbols);
}
let mut expanded: Vec<&str> = vec![];
for s in symbols {
let s = s.as_ref();
if ops.contains_key(s) || s.parse::<i32>().is_ok() {
expanded.push(s);
} else {
let mut resolved = user_ops
.get(s)
.ok_or(Error::Bad)?
.split_ascii_whitespace()
.collect::<Vec<_>>();
expanded.append(&mut resolved);
}
}
expand_symbols(&expanded, ops, user_ops)
}
I get:
error[E0515]: cannot return value referencing local variable `expanded`
--> src/main.rs:32:5
|
32 | expand_symbols(&expanded, ops, user_ops)
| ^^^^^^^^^^^^^^^---------^^^^^^^^^^^^^^^^
| | |
| | `expanded` is borrowed here
| returns a value referencing data owned by the current function
For more information about this error, try `rustc --explain E0515`.
However, if I change the last statement to:
Ok(expanded)
it works, but it's not longer recursive.
I understand the idea that I'm trying to return a value borrowed from a local frame, but I think this is safe based on the second example. How can I tell the compiler that?
Note: I'm using AsRef because I want to be able to pass both a Vec<String> and Vec<&str> to expand_symbols(). Maybe I need to forget about that?
With Ok(expanded) the variable expanded is moved out of the function, meaning no reference to it existing after the function returned. So, the second sample, if you meant by Ok(expanded), is not same as the original one.
To address the issue I think you can pass a mutable reference to symbols as the first parameter of the function, and do in-place edit on it instead of creating a new local vector, explanded.
fn expand_symbols<'a>(
symbols: &'a mut Vec<&'a str>,
ops: &HashMap<String, String>,
user_ops: &'a HashMap<String, String>,
) -> std::result::Result<&'a Vec<&'a str>, Error> {
if symbols.is_empty() || symbols.iter().all(|x| ops.get(*x).is_some()) {
return Ok(symbols);
}
let mut unresolved: Vec<&str> = vec![];
let mut i = 0;
while i < symbols.len() {
let s = symbols[i];
if ops.contains_key(s) || s.parse::<i32>().is_ok() {
i += 1;
} else {
unresolved.push(symbols.remove(i));
}
}
for s in unresolved.iter() {
let mut resolved = user_ops
.get(*s)
.ok_or(Error::Bad)?
.split_ascii_whitespace()
.collect::<Vec<_>>();
symbols.append(&mut resolved);
};
expand_symbols(symbols, ops, user_ops)
}
playground

How to fix use of moved value in Rust?

I am trying to convert a yaml file to xml using Rust and I am not able to figure out how to fix this error regarding the use of moved value. I think I understand why this error is coming, but haven't got a clue about what to do next.
Here's the code:
struct Element {
element_name: String,
indentation_count: i16,
}
struct Attribute<'a> {
attribute_name: &'a str,
attribute_value: &'a str,
}
fn convert_yaml_to_xml(content: String, indentation_count: i16) -> String {
let mut xml_elements: Vec<Element> = vec![];
let mut attributes: Vec<Attribute> = vec![];
xml_elements.push(Element {element_name: "xmlRoot".to_string(), indentation_count: -1});
let mut target: Vec<u8> = Vec::new();
let mut xml_data_writer = EmitterConfig::new().perform_indent(true).create_writer(&mut target);
let mut attribute_written_flag = false;
let mut xml_event;
xml_event = XmlEvent::start_element("xmlRoot");
for line in content.lines() {
let current_line = line.trim();
let caps = indentation_count_regex.captures(current_line).unwrap();
let current_indentation_count = caps.get(1).unwrap().as_str().to_string().len() as i16;
if ELEMENT_REGEX.is_match(current_line) {
loop {
let current_attribute_option = attributes.pop();
match current_attribute_option {
Some(current_attribute_option) => {
xml_event.attr(current_attribute_option.attribute_name, current_attribute_option.attribute_value)
},
None => {
break;
},
};
}
xml_data_writer.write(xml_event);
// Checking if the line is an element
let caps = ELEMENT_REGEX.captures(current_line).unwrap();
let element_name = caps.get(2);
let xml_element_struct = Element {
indentation_count: current_indentation_count,
element_name: element_name.unwrap().as_str().to_string(),
};
xml_elements.push(xml_element_struct);
xml_event = XmlEvent::start_element(element_name.unwrap().as_str());
attribute_written_flag = false;
} else if ATTR_REGEX.is_match(current_line) {
// Checking if the line is an attribute
let caps = ATTR_REGEX.captures(current_line).unwrap();
let attr_name = caps.get(2);
let attr_value = caps.get(3);
// Saving attributes to a stack
attributes.push(Attribute{ attribute_name: attr_name.unwrap().as_str(), attribute_value: attr_value.unwrap().as_str() });
// xml_event.attr(attr_name.unwrap().as_str(), attr_value.unwrap().as_str());
}/* else if NEW_ATTR_SET_REGEX.is_match(current_line) {
let caps = NEW_ATTR_SET_REGEX.captures(current_line).unwrap();
let new_attr_set_name = caps.get(2);
let new_attr_set_value = caps.get(3);
current_xml_hash.insert("name".to_string(), new_attr_set_name.unwrap().as_str().to_string());
current_xml_hash.insert("value".to_string(), new_attr_set_value.unwrap().as_str().to_string());
} */
}
if attribute_written_flag {
xml_data_writer.write(xml_event);
}
for item in xml_elements.iter() {
let event = XmlEvent::end_element();
let event_name = item.element_name.to_string();
xml_data_writer.write(event.name(event_name.as_str()));
}
println!("OUTPUT");
println!("{:?}", target);
return "".to_string();
}
And here's the error:
error[E0382]: use of moved value: `xml_event`
--> src/main.rs:77:25
|
65 | let mut xml_event;
| ------------- move occurs because `xml_event` has type `StartElementBuilder<'_>`, which does not implement the `Copy` trait
...
77 | xml_event.attr(current_attribute_option.attribute_name, current_attribute_option.attribute_value)
| ^^^^^^^^^ --------------------------------------------------------------------------------------- `xml_event` moved due to this method call, in previous iteration of loop
|
note: this function takes ownership of the receiver `self`, which moves `xml_event`
--> /Users/defiant/.cargo/registry/src/github.com-1ecc6299db9ec823/xml-rs-0.8.4/src/writer/events.rs:193:24
|
193 | pub fn attr<N>(mut self, name: N, value: &'a str) -> StartElementBuilder<'a>
| ^^^^
From XmlEvent::start_element() documentation we see that it produces a StartElementBuilder<'a>.
From StartElementBuilder<'a>::attr() documentation we see that it consumes the StartElementBuilder<'a> (the first parameter is self, not &mut self) and produces a new StartElementBuilder<'a> (which is probably similar to self but considers the expected effect of .attr()).
This approach is known as the consuming builder pattern, which is used in Rust (for example std::thread::Builder).
The typical usage of such an approach consists in chaining the function calls: something.a().b().c().d() such as something is consumed by a(), its result is consumed by b(), the same about c() and finally d() does something useful with the last result.
The alternative would be to use mutable borrows in order to modify in place something but dealing with mutable borrows is known as difficult in some situations.
In your case, you can just reassign the result of .attr() to xml_event because otherwise the .attr() function would have no effect (its result is discarded) and xml_event would become unusable because it is consumed; reassigning it makes it usable again afterwards (at least i guess, i didn't try).

Is there a way to release a binding before it goes out of scope?

I'm trying to parse a file using regexes:
extern crate regex; // 1.0.1
use regex::Regex;
fn example(
section_header_pattern: Regex,
section_name: &str,
mut line: String,
mut is_in_right_section: bool,
) {
loop {
if let Some(m) = section_header_pattern
.captures(&line)
.and_then(|c| c.get(1))
{
is_in_right_section = m.as_str().eq(section_name);
line.clear();
continue;
}
}
}
fn main() {}
...but the compiler complains because the RegEx's captures() method has a borrow which endures for the lifetime of the match:
error[E0502]: cannot borrow `line` as mutable because it is also borrowed as immutable
--> src/main.rs:17:13
|
13 | .captures(&line)
| ---- immutable borrow occurs here
...
17 | line.clear();
| ^^^^ mutable borrow occurs here
18 | continue;
19 | }
| - immutable borrow ends here
By the time I get to line.clear();, I'm done with the Match and would like to clear the buffer and move onto the next line in the file without further processing. Is there a good/clean/elegant/idiomatic solution or do I need to just bite the bullet and introduce a subsequent 'if' block?
Short answer: No.
I'm done with the Match
You may be, but the compiler doesn't know that. Specifically, lifetimes are currently bound to the lexical scope they are defined in. The feature you are looking for is called non-lexical lifetimes. It's not stable now, but it's planned to be enabled in the Rust 2018 edition.
As an example:
fn main() {
let mut s = String::from("hello");
let matched = &s[..];
println!("{}", matched);
s.clear();
println!("{}", s);
}
A programmer can tell we are done with matched after we print it, but the compiler says that the borrow lasts until the closing }. The fix is to introduce a scope:
fn main() {
let mut s = String::from("hello");
{
let matched = &s[..];
println!("{}", matched);
}
s.clear();
println!("{}", s);
}
Your case is more insidious, as the decision to clear the string is interwoven with the value of the borrow of the string itself. Something like this would be my first place to reach:
fn main() {
let mut s = String::from("hello");
let do_clear;
{
let matched = &s[..];
println!("{}", matched);
do_clear = matched.contains("ll");
}
if do_clear {
s.clear();
}
println!("{}", s);
}
However, your specific case might be able to be transformed to avoid multiple if / if let statements:
let is_in_right_section = section_header_pattern.captures(&line)
.and_then(|c| c.get(1))
.map_or(false, |m| m.as_str() == section_name);
if is_in_right_section {
line.clear();
continue;
}
Which wouldn't look too bad if you introduce a new type and/or method. As a bonus, there's a place for the Regex to live:
struct Section(Regex);
impl Section {
fn is(&self, s: &str, section: &str) -> bool {
self.0
.captures(s)
.and_then(|c| c.get(1))
.map_or(false, |m| m.as_str() == section)
}
}
// ----
if section.is(&line, section_name) {
line.clear();
continue;
}
The original code works as-is when NLL is enabled:
#![feature(nll)]
extern crate regex; // 1.0.1
use regex::Regex;
fn main() {
let section_header_pattern = Regex::new(".").unwrap();
let section_name = "";
let mut line = String::new();
let mut is_in_right_section = false;
loop {
if let Some(m) = section_header_pattern
.captures(&line)
.and_then(|c| c.get(1))
{
is_in_right_section = m.as_str().eq(section_name);
line.clear();
continue;
}
return; // I don't really want to loop
}
}

Resources