Evaluate string expression in rust with conditionals - rust

I am trying to evaluate a string in rust with conditionals using evalexpr module.
This results in the following error
thread 'main' panicked at 'called Result::unwrap() on an Err value: AppendedToLeafNode', src\main.rs:6:56
The error description says
https://docs.rs/evalexpr/5.0.5/evalexpr/error/enum.EvalexprError.html#variant.AppendedToLeafNode
Tried to append a child to a leaf node. Leaf nodes cannot have
children.
I am not able to understand why this error is happening. Does anyone have a clue? Is it possible to evaluate an string with conditionals in Rust?
use evalexpr::*;
fn main() {
let evalstr = "let x = if input == 8 { 3 } else { 4 };";
let mut context = HashMapContext::new();
context.set_value("input".into(), 8.into()).unwrap();
eval_empty_with_context_mut(evalstr, &mut context).unwrap();
let val = context.get_value("x").unwrap();
println!("{}", val);
}

evalexpr doesn't support the Rust language, but uses its own little language documented here: https://docs.rs/evalexpr/6.6.0/evalexpr/index.html#features
That little language does not support conditionals yet: https://github.com/ISibboI/evalexpr/issues/82

Related

Rust Lang: What is "if let Some(x) = x" doing?

I'm working on a rust project written a couple of years ago, and have come across this piece of code, which is literally:
let mut values = vec![];
for x in maybe_values {
if let Some(x) = x {
values.push(Arc::new(x));
}
}
I understand that "if let" introduces a pattern-matching if (Which seems to be a poor re-use of the keyword "let", but I will get over that - If anyone can help me with a mental mnemonic to make sense of "let" here, please do!).
But what is the test Some(x) = x doing?
From my testing, it seems to be a trick/idiom to both a) test that the loop variant 'x' is Some(), and also b) end up with the unwrap()ped value in x.
But I can't fully explain it to myself, and can't find reference to this being an idiom anywhere.
Hope you can help my Rust education path. Thanks.
This is a shorthand for using a full match statement when you only care about matching a single use case.
So this block of code:
if let x = y {
foo();
} else {
bar();
}
Is equivalent to using a full match:
match y {
x => {
foo();
}
_ => {
bar();
}
}
For your specific case, it is equivalent to this. The inner x uses the same name as the outer variable which can be confusing, but they are two separate values.
let mut values = vec![];
for x in maybe_values {
match x {
Some(y) => values.push(Arc::new(y)),
_ => {},
}
}
There are two completely different variables in play here. It's equivalent to.
let mut values = vec![];
for x_1 in maybe_values {
if let Some(x_2) = x_1 {
values.push(Arc::new(x_2));
}
}
In Rust, the right-hand side of a let is evaluated with the left-hand variable not in scope, so when the if let is evaluated, the outer x is still in-scope. Then, if it's a Some value, we make a new variable x which contains the inside of the Option. This variable shadows the previous x, making it inaccessible inside the if statement (in the same way that a function argument called x would render a global variable named x inaccessible by shadowing).

Can/does rust have a C question mark colon equivalent [duplicate]

How can I port this C++ code to Rust:
auto sgnR = (R >= 0.) ? 1. : -1.;
I have seen some examples with the match keyword, but I don't understand how it works.
Rust does not have the ternary operator because it's not needed. Everything evaluates to some value, and if / else statements are no exception:
let r = 42.42;
let sgn_r = if r >= 0. { 1. } else { -1. };
You'll note that I've also changed your variable names to be idiomatic Rust. Identifiers use snake_case.
Do not be confused by the ? operator that Rust does have. This is called the "try operator" and is used to propagate errors.
Specifically for this code, it's likely you should use f64::signum:
let r = 42.42_f64;
let sgn_r = r.signum();
You can use bool::then or in this case, the non-lazy bool::then_some to accomplish the same thing:
let sgn_r = (r >= 0).then_some(1).unwrap_or(-1);
An if/else statement is probably better for readability, but this may be nicer in certain cases.
I like to implement a similar construct using a trait:
pub trait IfElse {
fn ifelse<T>(&self, iftrue: T, iffalse: T) -> T;
}
impl IfElse for bool {
fn ifelse<T>(&self, iftrue: T, iffalse: T) -> T {
if *self { iftrue }
else { iffalse }
}
}
Then it can be used like this:
let sng_r = (r >= 0).ifelse(1, -1);

Rust String concatenation [duplicate]

This question already has answers here:
How do I concatenate strings?
(9 answers)
Closed 7 years ago.
I started programming with Rust this week and I am having a lot of problems understanding how Strings work.
Right now, I am trying to do a simple program that prints a list of players appending their order(for learning purposes only).
let res : String = pl.name.chars().enumerate().fold(String::new(),|res,(i,ch)| -> String {
res+=format!("{} {}\n",i.to_string(),ch.to_string());
});
println!("{}", res);
This is my idea, I know I could just use a for loop but the objective is to understand the different Iterator functions.
So, my problem is that the String concatenation does not work.
Compiling prueba2 v0.1.0 (file:///home/pancho111203/projects/prueba2)
src/main.rs:27:13: 27:16 error: binary assignment operation `+=` cannot be applied to types `collections::string::String` and `collections::string::String` [E0368]
src/main.rs:27 res+=format!("{} {}\n",i.to_string(),ch.to_string());
^~~
error: aborting due to previous error
Could not compile `prueba2`.
I tried using &str but it is not possible to create them from i and ch values.
First, in Rust x += y is not overloadable, so += operator won't work for anything except basic numeric types. However, even if it worked for strings, it would be equivalent to x = x + y, like in the following:
res = res + format!("{} {}\n",i.to_string(),ch.to_string())
Even if this were allowed by the type system (it is not because String + String "overload" is not defined in Rust), this is still not how fold() operates. You want this:
res + &format!("{} {}\n", i, ch)
or, as a compilable example,
fn main(){
let x = "hello";
let res : String = x.chars().enumerate().fold(String::new(), |res, (i, ch)| {
res + &format!("{} {}\n", i, ch)
});
println!("{}", res);
}
When you perform a fold, you don't reassign the accumulator variable, you need to return the new value for it to be used on the next iteration, and this is exactly what res + format!(...) do.
Note that I've removed to_string() invocations because they are completely unnecessary - in fact, x.to_string() is equivalent to format!("{}", x), so you only perform unnecessary allocations here.
Additionally, I'm taking format!() result by reference: &format!(...). This is necessary because + "overload" for strings is defined for String + &str pair of types, so you need to convert from String (the result of format!()) to &str, and this can be done simply by using & here (because of deref coercion).
In fact, the following would be more efficient:
use std::fmt::Write;
fn main(){
let x = "hello";
let res: String = x.chars().enumerate().fold(String::new(), |mut res, (i, ch)| {
write!(&mut res, "{} {}\n", i, ch).unwrap();
res
});
println!("{}", res);
}
which could be written more idiomatically as
use std::fmt::Write;
fn main(){
let x = "hello";
let mut res = String::new();
for (i, ch) in x.chars().enumerate() {
write!(&mut res, "{} {}\n", i, ch).unwrap();
}
println!("{}", res);
}
(try it on playpen)
This way no extra allocations (i.e. new strings from format!()) are created. We just fill the string with the new data, very similar, for example, to how StringBuilder in Java works. use std::fmt::Write here is needed to allow calling write!() on &mut String.
I would also suggest reading the chapter on strings in the official Rust book (and the book as a whole if you're new to Rust). It explains what String and &str are, how they are different and how to work with them efficiently.

Why doesn't this compile - use of undeclared type name `thread::scoped`

I'm trying to get my head around Rust. I've got an alpha version of 1.
Here's the problem I'm trying to program: I have a vector of floats. I want to set up some threads asynchronously. Each thread should wait for the number of seconds specified by each element of the vector, and return the value of the element, plus 10. The results need to be in input order.
It's an artificial example, to be sure, but I wanted to see if I could implement something simple before moving onto more complex code. Here is my code so far:
use std::thread;
use std::old_io::timer;
use std::time::duration::Duration;
fn main() {
let mut vin = vec![1.4f64, 1.2f64, 1.5f64];
let mut guards: Vec<thread::scoped> = Vec::with_capacity(3);
let mut answers: Vec<f64> = Vec::with_capacity(3);
for i in 0..3 {
guards[i] = thread::scoped( move || {
let ms = (1000.0f64 * vin[i]) as i64;
let d = Duration::milliseconds(ms);
timer::sleep(d);
println!("Waited {}", vin[i]);
answers[i] = 10.0f64 + (vin[i] as f64);
})};
for i in 0..3 {guards[i].join(); };
for i in 0..3 {println!("{}", vin[i]); }
}
So the input vector is [1.4, 1.2, 1.5], and I'm expecting the output vector to be [11.4, 11.2, 11.5].
There appear to be a number of problems with my code, but the first one is that I get a compilation error:
threads.rs:7:25: 7:39 error: use of undeclared type name `thread::scoped`
threads.rs:7 let mut guards: Vec<thread::scoped> = Vec::with_capacity(3);
^~~~~~~~~~~~~~
error: aborting due to previous error
There also seem to be a number of other problems, including using vin within a closure. Also, I have no idea what move does, other than the fact that every example I've seen seems to use it.
Your error is due to the fact that thread::scoped is a function, not a type. What you want is a Vec<T> where T is the result type of the function. Rust has a neat feature that helps you here: It automatically detects the correct type of your variables in many situations.
If you use
let mut guards = Vec::with_capacity(3);
the type of guards will be chosen when you use .push() the first time.
There also seem to be a number of other problems.
you are accessing guards[i] in the first for loop, but the length of the guards vector is 0. Its capacity is 3, which means that you won't have any unnecessary allocations as long as the vector never contains more than 3 elements. use guards.push(x) instead of guards[i] = x.
thread::scoped expects a Fn() -> T, so your closure can return an object. You get that object when you call .join(), so you don't need an answer-vector.
vin is moved to the closure. Therefore in the second iteration of the loop that creates your guards, vin isn't available anymore to be moved to the "second" closure. Every loop iteration creates a new closure.
i is moved to the closure. I have no idea what's going on there. But the solution is to let inval = vin[i]; outside the closure, and then use inval inside the closure. This also solves Point 3.
vin is mutable. Yet you never mutate it. Don't bind variables mutably if you don't need to.
vin is an array of f64. Therefore (vin[i] as f64) does nothing. Therefore you can simply use vin[i] directly.
join moves out of the guard. Since you cannot move out of an array, your cannot index into an array of guards and join the element at the specified index. What you can do is loop over the elements of the array and join each guard.
Basically this means: don't iterate over indices (for i in 1..3), but iterate over elements (for element in vector) whenever possible.
All of the above implemented:
use std::thread;
use std::old_io::timer;
use std::time::duration::Duration;
fn main() {
let vin = vec![1.4f64, 1.2f64, 1.5f64];
let mut guards = Vec::with_capacity(3);
for inval in vin {
guards.push(thread::scoped( move || {
let ms = (1000.0f64 * inval) as i64;
let d = Duration::milliseconds(ms);
timer::sleep(d);
println!("Waited {}", inval);
10.0f64 + inval
}));
}
for guard in guards {
let answer = guard.join();
println!("{}", answer);
};
}
In supplement of Ker's answer: if you really need to mutate arrays within a thread, I suppose the most closest valid solution for your task will be something like this:
use std::thread::spawn;
use std::old_io::timer;
use std::sync::{Arc, Mutex};
use std::time::duration::Duration;
fn main() {
let vin = Arc::new(vec![1.4f64, 1.2f64, 1.5f64]);
let answers = Arc::new(Mutex::new(vec![0f64, 0f64, 0f64]));
let mut workers = Vec::new();
for i in 0..3 {
let worker_vin = vin.clone();
let worker_answers = answers.clone();
let worker = spawn( move || {
let ms = (1000.0f64 * worker_vin[i]) as i64;
let d = Duration::milliseconds(ms);
timer::sleep(d);
println!("Waited {}", worker_vin[i]);
let mut answers = worker_answers.lock().unwrap();
answers[i] = 10.0f64 + (worker_vin[i] as f64);
});
workers.push(worker);
}
for worker in workers { worker.join().unwrap(); }
for answer in answers.lock().unwrap().iter() {
println!("{}", answer);
}
}
In order to share vectors between several threads, I have to prove, that these vectors outlive all of my threads. I cannot use just Vec, because it will be destroyed at the end of main block, and another thread could live longer, possibly accessing freed memory. So I took Arc reference counter, which guarantees, that my vectors will be destroyed only when the counter downs to zero.
Arc allows me to share read-only data. In order to mutate answers array, I should use some synchronize tools, like Mutex. That is how Rust prevents me to make data races.

Variables with the same name inside Some(T) in Rust

I'm fairly new to Rust so I came across this piece of code in the official Guide
let input = io::stdin().read_line()
.ok()
.expect("Failed to read line");
let input_num: Option<uint> = from_str(input.as_slice());
let num = match input_num {
Some(num) => num,
None => {
println!("Please input a number!");
return;
}
};
While the understand the first two statements (on input and inputnum), I'm not quite sure about the match statement. So I checked the documentation which shows that Option<T> can take two values , either None or Some(T) for some (object?) T. So I tested the following code:
io::println(
match input_num {
Some(num) => "somenum",
None => {
println!("Please input a number only!");
return;
}
}
);
This code works as expected; it prints somenum if you enter a number and otherwise it prints the error message. However, the compiler gives a warning stating: warning: unused variable:num, #[warn(unused_variable)] on by default. This confirmed my suspicions that the num inside the `match is used as a variable.
Question: How is it possible that rust does not complain about (in the Guide's example) having two variables with the same name num? Or does it "hand over" the pointer to the inside num to the outside num?
Also in the case of an empty return what exactly is returned? I'm guessing it is unit () because it is mentioned here that
functions without a -> ... implicitly have return type ()
Edit: Sorry for missing the obvious point. The return directly exits the function without bothering to put anything into num.
P.S. I noticed that using cargo build to compile does not give warnings the second time around (not making any changes). Does cargo keep tracking of versions or something?
Do you have any experiences with Java or C#? Many mainstream programing languages allows you to shadow a variable with another variable with the same name in a new block scope. For example, take a look at this C# code:
using System;
public class Test
{
public static void Main()
{
int x = 10;
{
int x = 20;
Console.WriteLine(x);
}
Console.WriteLine(x);
}
}
http://ideone.com/OuMlc8
In fact Rust is more flexible in terms of shadowing: you can shadow variables as many times as you want without explicitly making a brace block.
fn main() {
let i = 10i32;
let i = "foo";
println!("{}", i);
}
http://is.gd/CUHUOL
Here, the first i and the second i have no relationships. We just lose a way to use (refer to) the first i.
This confirmed my suspicions that the num inside the `match is used as a variable.
Yes, pattern matching can introduce new variables. But the new variable num introduced by pattern matching has no direct relationship with the outer num whose value is being given by the match expression.
I can translate your Rust code into less idiomatic Rust to illustrate what was happening:
let num =
if input_num.is_some() {
let num = input_num.unwrap();
num
} else {
println!("Please input a number!");
return;
}
;
As for not observing warnings from cargo build, I suspect it was because Cargo had already built .o from your code and didn't bother to repeat the same job twice, knowing that there was no change in your code.

Resources