Using a Result by reference if it's Ok - rust

I want to alter parameters list if some environment variable is set. The parameters list is a vector of tuples with two string slices each.
The code that supposed to do that:
use std::env;
fn main() {
let mut params = vec![
// there is a whole list of default params
("param", "value"),
];
if let Ok(extra_param) = env::var("EXTRA_PARAM") {
params.push(("extra_param", &extra_param));
};
dbg!(params);
}
Of course, it doesn't work because extra_params belongs to the local if let scope, and the compiler produces the following error:
$ EXTRA_PARAM=extra_value cargo run
error[E0597]: `extra_param` does not live long enough
--> src/main.rs:10:37
|
10 | params.push(("extra_param", &extra_param));
| ^^^^^^^^^^^^ borrowed value does not live long enough
11 | };
| - `extra_param` dropped here while still borrowed
12 |
13 | dbg!(params);
| ------ borrow later used here
For more information about this error, try `rustc --explain E0597`.
error: could not compile `question` due to previous error
I solved this by adding a variable into the outer scope and copying the internal variable's value before referencing it:
let external_param;
if let Ok(extra_param) = env::var("EXTRA_PARAM") {
external_param = extra_param;
params.push(("extra_param", &external_param));
};
Now, my program works as expected, but the solution feels mediocre.
I'm I missing something? Is there any way to get the Result so the code would look less clumsy?
I want to solve this without changing types inside tuples, so they still would be string slices, because there are lots of them that are defined in code and I only need one extra parameter to add conditionally.
Rust playground link

I see three solution you could use:
Create the Result as a variable outside of if let statement:
let envvar = env::var("EXTRA_PARAM");
if let Ok(ref extra_param) = envvar {
params.push(("extra_param", extra_param));
}
Use the fact that Option and Result implement Iterator to extend the Vec if envvar is Ok:
let envvar = env::var("EXTRA_PARAM");
params.extend(envvar.as_ref().map(|extra| ("extra_param", extra.as_str())));
Change the params Vec to own String instead of &str:
fn main() {
let mut params = vec![
// there is a whole list of default params
("param", "value".to_owned()),
];
if let Ok(extra_param) = env::var("EXTRA_PARAM") {
// now extra param is moved into params instead of borrowed
params.push(("extra_param", extra_param));
};
// or use this instead of if let
params.extend(env::var("EXTRA_PARAM").map(|extra_param| ("extra_param", extra_param)));
dbg!(params);
}

From the comments I got to my question, it seems OK if I use the external variable. The only more concise way I found is to extract the parameter first using unwrap_or_default and to compare it with an empty string in the condition:
use std::env;
fn main() {
let mut params = vec![
// there is a whole list of default params
("param", "value"),
];
let extra_param = env::var("EXTRA_PARAM").unwrap_or_default();
if extra_param != "" {
params.push(("extra_param", &extra_param));
};
dbg!(params);
}
It makes even more sense if I decide to extract params into a separate function, because extracting the value from Result inside would mean creating a local variable, so I couldn't return a reference to the parameter this way:
use std::env;
fn main() {
let extra_param = env::var("EXTRA_PARAM").unwrap_or_default();
let params = build_params(&extra_param);
dbg!(params);
}
fn build_params<'a>(extra_param: &'a String) -> Vec<(&'static str, &'a str)> {
let mut params = vec![
// there is a whole list of default params
("param", "value"),
];
if extra_param != "" {
params.push(("extra_param", extra_param));
};
params
}

Related

Rust multithread intensive methods which need to access same set of data with Rayon

I use Rayons par_iter()to iterate over different variations of the expensive method I need to run. These runs need to access the same set of checked usizes because they all need to add to it and check it from time to time. I also need them all to shutdown when first thread finishes, this is why I have a kill_switch which will force the iterations to exit when its set to true.
let mut checked: HashSet<usize> = HashSet::new();
let mut kill_switch: bool = false;
permutations.par_iter().for_each(|order| {
let board = Board::new(board_map.clone(), order.clone());
let mut bubbles: Vec<(i8, i8)> = Vec::new();
if let Some(bubbles) = board.solve(&mut bubbles, &mut checked, &kill_switch) {
kill_switch = true;
bubbles.into_iter().for_each(|bubble| {
dbg!(bubble);
});
}
})
This is the code I currently have but I get errors for how I'm using checked and kill_switch. How do I make this work?
Errors:
cannot borrow checked as mutable, as it is a captured variable in a Fn closure
cannot borrow as mutable [E0596]
cannot assign to kill_switch, as it is a captured variable in a Fn closure
cannot assign [E0594]
To fix the errors, you will need to use RefCells to wrap the checked and kill_switch variables and use the borrow_mut method to get a mutable reference to them in the closure.
Here is an example of how you can modify your code:
use std::cell::RefCell;
use std::collections::HashSet;
let checked: RefCell<HashSet<usize>> = RefCell::new(HashSet::new());
let kill_switch: RefCell<bool> = RefCell::new(false);
permutations.par_iter().for_each(|order| {
let board = Board::new(board_map.clone(), order.clone());
let mut bubbles: Vec<(i8, i8)> = Vec::new();
if let Some(bubbles) = board.solve(&mut bubbles, &mut checked.borrow_mut(), &mut kill_switch.borrow_mut()) {
*kill_switch.borrow_mut() = true;
bubbles.into_iter().for_each(|bubble| {
dbg!(bubble);
});
}
})
Note that you will also need to add RefCell as a dependency in your project.

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).

What is an alternative for conditional evaluation like if or match in a Rust const function?

I have code which reads a config.toml file based on the environment name and provides all the config settings to the entire project.
const fn get_conf() -> toml::Value {
let file_name = match env::var("ENVIRONMENT") {
Ok(val) => val.to_lowercase(),
Err(_e) => "development".to_lowercase(),
};
let content = fs::read_to_string(format!("conf/{}.toml", file_name)).unwrap();
let conf: Value = toml::from_str(content.as_str()).unwrap();
conf
}
static mut CONFIG: toml::Value = get_conf();
I get an error:
error[E0658]: `match` is not allowed in a `const fn`
--> src/lib.rs:2:21
|
2 | let file_name = match env::var("ENVIRONMENT") {
| _____________________^
3 | | Ok(val) => val.to_lowercase(),
4 | | Err(_e) => "development".to_lowercase(),
5 | | };
| |_____^
|
= note: see issue #49146 <https://github.com/rust-lang/rust/issues/49146> for more information
This is solved in Rust nightly, but I don't want to use a nightly build for production. Is there any workaround for using a match or if condition in a const function?
From Rust 1.46 some core language features are now allowed in const fn.
const fn improvements
There are several core language features you can now use in a const
fn:
if, if let, and match
while, while let, and loop
the && and || operators
The primary alternative for conditionals in const expressions is to cast booleans into usizes and use them as an index into an array of resulting values:
const fn demo(is_enabled: bool) -> i32 {
let choices = [
0, // false
42, // true
];
choices[is_enabled as usize]
}
This can be (painfully) expanded to more and more conditions:
const fn sign(count: i32) -> i32 {
let is_pos = count > 0;
let is_zero = count == 0;
[[-1, 0], [1, 0]][is_pos as usize][is_zero as usize]
}
fn main() {
dbg!(sign(-2));
dbg!(sign(-0));
dbg!(sign(2));
}
See also:
Calculating maximum value of a set of constant expressions at compile time
How do I convert a boolean to an integer in Rust?
For your actual use case, you should just use a lazy global value:
use once_cell::sync::Lazy; // 1.4.0
use std::{env, fs};
static mut CONFIG: Lazy<toml::Value> = Lazy::new(|| {
let file_name = match env::var("ENVIRONMENT") {
Ok(val) => val.to_lowercase(),
Err(_e) => "development".to_lowercase(),
};
let content = fs::read_to_string(format!("conf/{}.toml", file_name)).unwrap();
toml::from_str(content.as_str()).unwrap()
});
See also:
How do I create a global, mutable singleton?
It is not possible to use a const-fn with branching on stable yet indeed. Moreover, you are using env::var which is not const-fn, thus cannot be used in other const-fn. I believe you meant std::env! instead, which retrieves an environment variable during compile-time, rather than execution.
Fow now one possible "workaround" is a build script, it's a little bit clumsy yet powerful. With this approach flow would be as following:
// build.rs
use std::path::Path;
use std::env;
use std::fs::File;
fn main() {
let path = Path::new(env::var("OUT_DIR").unwrap().as_str()).join("gen.rs");
File::create(path)
.expect("gen.rs create failed")
.write_all("<your generated content here>")
.expect("gen file write failed")
}
Then you can use generated file with a direct file-include:
// main.rs or any other file
include!(concat!(env!("OUT_DIR"), "/gen.rs"));

Why does the Rust compiler complain that I use a moved value when I've replaced it with a new value?

I am working on two singly linked lists, named longer and shorter. The length of the longer one is guaranteed to be no less than the shorter one.
I pair the lists element-wise and do something to each pair. If the longer list has more unpaired elements, process the rest of them:
struct List {
next: Option<Box<List>>,
}
fn drain_lists(mut shorter: Option<Box<List>>, mut longer: Option<Box<List>>) {
// Pair the elements in the two lists.
while let (Some(node1), Some(node2)) = (shorter, longer) {
// Actual work elided.
shorter = node1.next;
longer = node2.next;
}
// Process the rest in the longer list.
while let Some(node) = longer {
// Actual work elided.
longer = node.next;
}
}
However, the compiler complains on the second while loop that
error[E0382]: use of moved value
--> src/lib.rs:13:20
|
5 | fn drain_lists(mut shorter: Option<Box<List>>, mut longer: Option<Box<List>>) {
| ---------- move occurs because `longer` has type `std::option::Option<std::boxed::Box<List>>`, which does not implement the `Copy` trait
6 | // Pair the elements in the two lists.
7 | while let (Some(node1), Some(node2)) = (shorter, longer) {
| ------ value moved here
...
13 | while let Some(node) = longer {
| ^^^^ value used here after move
However, I do set a new value for shorter and longer at the end of the loop, so that I will never use a moved value of them.
How should I cater to the compiler?
I think that the problem is caused by the tuple temporary in the first loop. Creating a tuple moves its components into the new tuple, and that happens even when the subsequent pattern matching fails.
First, let me write a simpler version of your code. This compiles fine:
struct Foo(i32);
fn main() {
let mut longer = Foo(0);
while let Foo(x) = longer {
longer = Foo(x + 1);
}
println!("{:?}", longer.0);
}
But if I add a temporary to the while let then I'll trigger a compiler error similar to yours:
fn fwd<T>(t: T) -> T { t }
struct Foo(i32);
fn main() {
let mut longer = Foo(0);
while let Foo(x) = fwd(longer) {
longer = Foo(x + 1);
}
println!("{:?}", longer.0);
// Error: ^ borrow of moved value: `longer`
}
The solution is to add a local variable with the value to be destructured, instead of relying on a temporary. In your code:
struct List {
next: Option<Box<List>>
}
fn drain_lists(shorter: Option<Box<List>>,
longer: Option<Box<List>>) {
// Pair the elements in the two lists.
let mut twolists = (shorter, longer);
while let (Some(node1), Some(node2)) = twolists {
// Actual work elided.
twolists = (node1.next, node2.next);
}
// Process the rest in the longer list.
let (_, mut longer) = twolists;
while let Some(node) = longer {
// Actual work elided.
longer = node.next;
}
}
Other than getting rid of the tuple (shown by others), you can capture a mutable reference to the nodes:
while let (&mut Some(ref mut node1), &mut Some(ref mut node2)) = (&mut shorter, &mut longer) {
shorter = node1.next.take();
longer = node2.next.take();
}
The use of take() enables this to work: shorter = node1.next would complain of moving a field out of a reference, which is not allowed (it would leave the node in an undefined state). But takeing it is ok because it leaves None in the next field.
Looks like the destructuring on line 7 moves the value even when the block afterwards is not evaluated. (Edit: as #Sven Marnach pointed out in the comments, a temporary tuple gets created here which causes the move)
I've uglyfied your code to prove that point :)
struct List {
next: Option<Box<List>>
}
fn drain_lists(mut shorter: Option<Box<List>>,
mut longer: Option<Box<List>>) {
// Pair the elements in the two lists.
match(shorter, longer) {
(Some(node1), Some(node2)) => {
shorter = node1.next;
longer = node2.next;
},
(_, _) => return // without this you get the error
}
// Process the rest in the longer list.
while let Some(node) = longer {
// Actual work elided.
longer = node.next;
}
}
When I added the return for the default case, the code compiled.
One solution is to avoid the tuple and consequently the move of longer into the tuple.
fn actual_work(node1: &Box<List>, node2: &Box<List>) {
// Actual work elided
}
fn drain_lists(mut shorter: Option<Box<List>>, mut longer: Option<Box<List>>) {
while let Some(node1) = shorter {
if let Some(node2) = longer.as_ref() {
actual_work(&node1, node2);
}
shorter = node1.next;
longer = longer.map_or(None, move |l| {
l.next
});
}
// Process the rest in the longer list.
while let Some(node) = longer {
// Actual work elided.
longer = node.next;
}
}

How do I modify Rc<RefCell> from inside the closure?

I am trying to pass RefCell to a function in a closure and then modify the same variable from inside the closure. Here is my code:
let path: Rc<RefCell<Option<Vec<PathBuf>>>> = Rc::new(RefCell::new(None));
...
//valid value assigned to path
...
let cloned_path = path.clone();
button_run.connect_clicked(move |_| {
let to_remove: usize = open_dir(&mut cloned_path.borrow_mut().deref_mut());
//Here I need to remove "to_remove" index from cloned_path
});
//Choose a random directory from Vec and open it. Returns opened index.
fn open_dir(path_two: &mut Option<Vec<PathBuf>>) -> usize {
let vec = path_two.clone();
let vec_length = vec.unwrap().len();
let mut rng = thread_rng();
let rand_number = rng.gen_range(0, vec_length);
let p: &str = &*path_two.clone().expect("8")[rand_number].to_str().unwrap().to_string();
Command::new("explorer.exe").arg(p).output();
rand_number.clone()
}
First I thought that since my open_dir() function accepts &mut, I can modify the vector inside the function. But no matter what I tried I kept getting cannot move out of borrowed content error.
Then I thought - ok, I can return the index from the function and access cloned_path from the closure itself. But the only code that I could get to compile is
button_run.connect_clicked(move |_| {
let to_remove: usize = open_dir(&mut cloned_path.borrow_mut().deref_mut());
let x = &*cloned_path.borrow_mut().clone().unwrap().remove(to_remove);
});
It works, but it removes from a cloned version of cloned_path, leaving the original unaffected. Is there a way to access cloned_path directly to modify it's contents and if there is one, how do I approach this task?
The main way to modify contents of an enum value (and Option is enum) is pattern matching:
fn do_something(path_two: &mut Option<Vec<PathBuf>>) {
if let Some(ref mut paths) = *path_two {
paths.push(Path::new("abcde").to_path_buf());
}
}
Note that paths pattern variable is bound with ref mut qualifier - it means that it will be of type &mut Vec<PathBuf>, that is, a mutable reference to the internals of the option, exactly what you need to modify the vector, in case it is present.

Resources