"possibly uninitialized variable" when it is initialized inside `match` - rust

This code (playground):
fn main() {
let fruits = "banana";
let num = 20;
let result: i32;
match fruits {
"banana" => {
if num > 100 {
result = num * 3;
} else if num > 30 {
result = num * 4;
} else {
result = num * 5;
}
}
"apple" => {
if num > 100 {
result = num * 16;
} else if num > 30 {
result = num * 18;
} else {
result = num * 20;
}
}
_ => println!("wrong fruits option"),
}
// if num > 100 { result = num * 3; }
// else if num > 30 { result = num * 4; }
// else { result = num * 5;}
println!("{}", result)
}
Gives the following error:
error[E0381]: borrow of possibly-uninitialized variable: `result`
--> src/main.rs:31:20
|
31 | println!("{}", result)
| ^^^^^^ use of possibly-uninitialized `result`
|
= note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
As you can see result is initialized in match, so why is compiler giving me an error?
After changing the line
let result : i32;
to (playground)
let mut result = 0;
The code is compiling and working fine.
This is also working (playground):
fn main() {
// let fruits = "banana";
let num = 20;
let result: i32;
// match fruits {
// "banana" => {
// if num > 100 {
// result = num * 3;
// } else if num > 30 {
// result = num * 4;
// } else {
// result = num * 5;
// }
// }
// "apple" => {
// if num > 100 {
// result = num * 16;
// } else if num > 30 {
// result = num * 18;
// } else {
// result = num * 20;
// }
// }
// _ => println!("wrong fruits option"),
// }
if num > 100 {
result = num * 3;
} else if num > 30 {
result = num * 4;
} else {
result = num * 5;
}
println!("{}", result)
}

The reason result may be uninitialized is that the _ case does not initialize result.
However, I don't think your intention is to initialize result in that case. The real error here is probably that you probably intended the _ case to be an error case that stops the execution. However, you didn't tell it to stop the execution.
There is multiple ways to solve that:
Add a return or exit statement in the _ case
Replace the println with a panic
Introduce proper error handling by adding a Result return value to main and then return an error.
For the least amount of effort I decided to go with a panic. Just be aware that in production, panics should be the very last solution. Proper error handling with Result and Error is always preferred.
So here's my minimal modification to make it work:
fn main() {
let fruits = "banana";
let num = 20;
let result: i32;
match fruits {
"banana" => {
if num > 100 {
result = num * 3;
} else if num > 30 {
result = num * 4;
} else {
result = num * 5;
}
}
"apple" => {
if num > 100 {
result = num * 16;
} else if num > 30 {
result = num * 18;
} else {
result = num * 20;
}
}
_ => panic!("wrong fruits option"),
}
// if num > 100 { result = num * 3; }
// else if num > 30 { result = num * 4; }
// else { result = num * 5;}
println!("{}", result)
}

Related

How do i convert and calculate a string expression into arithmetic expression without external crate?

How do i convert and calculate a string expression into arithmetic expression without external crate
for example: “500+10-66*32”. expected result = 14208 (Do not want the precedence of operator)
//a = ‘+’, b = ‘-’, c = ‘*’, d = ‘/’, e = ‘(’, f = ‘)’
use std::collections::VecDeque;
fn calculate(s: String) -> i32 {
let mut multi_active = false;
const SPACE: char = ' ';
const SIGN_PLUS: char = '+';
const SIGN_MINUS: char = '-';
const SIGN_MULTIPLY: char = '*';
const SIGN_DIVIDE: char = '/';
const PAREN_OPEN: char = '(';
const PAREN_CLOSED: char = ')';
let len_s: usize = s.len();
let mut num: i32 = 0;
let mut ans: i32 = 0;
let mut sign: i32 = 1;
let mut stk: VecDeque<i32> = VecDeque::with_capacity(len_s);
stk.push_back(sign);
for ch in s.chars() {
println!("chars:{}",ch);
match ch {
'0'.. => {
num = num * 10 + (ch as i32 - '0' as i32);
println!("given numbers:{num}");
}
SIGN_PLUS | SIGN_MINUS => {
// println!("b4 ans = {ans}");
// println!("b4 sig = {sign}");
// println!("b4 num = {num}");
ans += sign * num;
sign = stk.back().unwrap() * if ch == SIGN_PLUS { 1 } else { -1 };
num = 0;
// println!("addition ans = {ans}");
// println!("multiply sig = {sign}");
// println!("multiply num = {num}");
multi_active = false;
}
PAREN_OPEN => {
stk.push_back(sign);
// println!("brak open");
// multi_active = false;
}
PAREN_CLOSED => {
stk.pop_back();
// multi_active = false;
}
SIGN_MULTIPLY => {
println!("b4 ans = {ans}"); //0 always
println!("b4 sig = {sign}"); // 1 always
println!("b4 num = {num}");// 10 first number
// 10 = 0 + 1 * 10
ans = ans + sign * num; // current ans = 10 target=>27
println!("simple multi- {}", ans);
//ans=3;
sign = stk.back().unwrap() * if ch == SIGN_MULTIPLY { 1 } else { -1 };
num = 0;
// println!("multiply ans = {ans}");
// println!("multiply sig = {sign}");
// println!("multiply num = {num}");
multi_active = true;
}
_ => {}
}
}
println!("final:{ans}####{sign}####{num}===={:?}",multi_active);
// if multi_active {
// // ans = (ans-3)*num+ 3;
// ans= ans*num;
// }
// else{
ans = ans + sign * num;
//}
ans
}
fn main() {
let inputs = "2+44+6+1".to_owned();
let outs = calculate(inputs);
println!("{outs}");
}
Expected Results
Input: “500+10-66*32”
Result: 14208
I have sucessfully implementde addition and subtraction, now stuck with order of precedence from left to right.
Here's a poor man's calculator. No negative values, no operator precedence, no parentheticals, and no graceful error handling; use at your own risk:
fn main() {
let e = "500+10-66*32";
let ops = ['+', '-', '*', '/'];
let values: Vec<f64> = e.split(&ops).map(|v| v.trim().parse().unwrap()).collect();
let operands: Vec<_> = e.matches(&ops).collect();
let (&(mut curr), values) = values.split_first().unwrap();
for (op, &value) in operands.into_iter().zip(values) {
match op {
"+" => { curr = curr + value },
"-" => { curr = curr - value },
"*" => { curr = curr * value },
"/" => { curr = curr / value },
_ => unreachable!(),
}
}
println!("{}", curr);
}
14208
Apart from a few compiles errors (empty char ''). You have no code to do the arithmetic. You are doing lexical manipulations.
I would give more advice but am stopped by confusion. I don't know what you are trying to do with the as and bs, etc.

Why is a return type of () expected even though it is set to bool?

In a function that is declared to return bool:
pub fn is_palindrome(num: u64) -> bool {
let mut digits = Vec::new();
let mut temp = num;
loop {
digits.push(temp % 10);
temp /= 10;
if temp == 0 {
break;
}
}
for i in 0..digits.len() / 2 {
if digits[i] != digits[digits.len() - i] {
false // HERE I GET THE ERROR !!!!!!!
}
}
true
}
I get an error while compiling:
error[E0308]: mismatched types
--> src/lib.rs:13:13
|
13 | false
| ^^^^^ expected (), found bool
|
= note: expected type `()`
found type `bool`
Why is this happening, and how to fix the problem?
The issue is because you have an early return and not placing the return keyword before it, as shown below:
if digits[i] != digits[digits.len() - i] {
return false
}
This is because all functions evaluate the last line as a return value. If you want to return before going through the last line, you should add the return keyword.
An additional input here is to refactor your code so that it only returns once:
pub fn is_palindrome(num: u64) -> bool {
let mut digits = Vec::new();
let mut temp = num;
let mut retval = true;
loop {
digits.push(temp % 10);
temp /= 10;
if temp == 0 {
break;
}
}
for i in 0..digits.len() / 2 {
if digits[i] != digits[digits.len() - i] {
retval = false; // you might want to put a break here so that it exits the loop after finding the first issue
}
}
retval
}

How to assign 2 variables simultaneously in Rust?

I'm currently teaching myself Rust with a roguelike tutorial, and I'm attempting to get a key press to move a character diagonally which would mean player_x -=1, player_y -= 1 for up left.
No matter which way I try to arrange the code, I keep getting error messages from the compiler. I couldn't find any example of this anywhere in the documentation or on GitHub.
Key { code: Escape, .. } => return true, // exit game
// movement keys
Key { code: Up, .. } => *player_y -= 1,
Key { code: Down, .. } => *player_y += 1,
Key { code: Left, .. } => *player_x -= 1,
Key { code: Right, .. } => *player_x += 1,
Key { printable: 'k', .. } => *player_y -= 1,
Key { printable: 'k', .. } => *player_x -= 1,
_ => {}
You can kind of see what I'm trying to do here, but this throws an error message saying the pattern is unreachable, how would I fix this code?
Here's the full code, it's fairly small, not sure what would be alone necessary to compile:
extern crate tcod;
extern crate input;
use tcod::console::*;
use tcod::colors;
// actual size of the window
const SCREEN_WIDTH: i32 = 80;
const SCREEN_HEIGHT: i32 = 50;
const LIMIT_FPS: i32 = 20; // 20 frames-per-second maximum
fn handle_keys(root: &mut Root, player_x: &mut i32, player_y: &mut i32) -> bool {
use tcod::input::Key;
use tcod::input::KeyCode::*;
let key = root.wait_for_keypress(true);
match key {
Key { code: Enter, alt: true, .. } => {
// Alt+Enter: toggle fullscreen
let fullscreen = root.is_fullscreen();
root.set_fullscreen(!fullscreen);
}
Key { code: Escape, .. } => return true, // exit game
// movement keys
Key { code: Up, .. } => *player_y -= 1,
Key { code: Down, .. } => *player_y += 1,
Key { code: Left, .. } => *player_x -= 1,
Key { code: Right, .. } => *player_x += 1,
Key { printable: 'k', ..} => *player_y -= 1,
Key { printable: 'k', ..} => *player_x -= 1,
_ => {},
}
false
}
fn main() {
let mut root = Root::initializer()
.font("terminal8x8_gs_tc.png", FontLayout::Tcod)
.font_type(FontType::Greyscale)
.size(SCREEN_WIDTH, SCREEN_HEIGHT)
.title("Rust/libtcod tutorial")
.init();
tcod::system::set_fps(LIMIT_FPS);
let mut player_x = SCREEN_WIDTH / 2;
let mut player_y = SCREEN_HEIGHT / 2;
while !root.window_closed() {
root.set_default_foreground(colors::WHITE);
root.put_char(player_x, player_y, '#', BackgroundFlag::None);
root.flush();
root.put_char(player_x, player_y, ' ', BackgroundFlag::None);
// handle keys and exit game if needed
let exit = handle_keys(&mut root, &mut player_x, &mut player_y);
if exit {
break
}
}
}
How to assign 2 variables simultaneously in Rust?
You cannot. You can bind two variables at once:
let (a, b) = (1, 2);
If you aren't trying to create new bindings, you need to have two assignment statements:
let mut a = 1;
let mut b = 2;
a = 3;
b = 4;
In your case, for a match statement, you need to introduce a block:
let key = 42;
let mut a = 1;
let mut b = 2;
match key {
0 => {
a += 1;
b -= 1;
}
_ => {
a -= 10;
b *= 100;
}
}
You could also have the match expression evaluate to a tuple, which you then create new bindings for and apply them afterwards:
let key = 42;
let mut x = 1;
let mut y = 2;
let (d_x, d_y) = match key {
0 => (1, -1),
_ => (10, 10),
};
x += d_x;
y += d_y;
I strongly recommend reading The Rust Programming Language instead of trying to learn Rust by intuition or trial and error. It has an entire chapter on the match statement.
See also:
How to swap two variables?
The second printable: 'k' is unreachable, since the first will match instead. What you want is doing both assignments in the same arm of the match, like this:
Key { printable: 'k', .. } => {
*player_y -= 1;
*player_x -= 1;
}

How do I get a substring between two patterns in Rust?

I want to create a substring in Rust. It starts with an occurrence of a string and ends at the end of the string minus four characters or at a certain character.
My first approach was
string[string.find("pattern").unwrap()..string.len()-5]
That is wrong because Rust's strings are valid UTF-8 and thus byte and not char based.
My second approach is correct but too verbose:
let start_bytes = string.find("pattern").unwrap();
let mut char_byte_counter = 0;
let result = line.chars()
.skip_while(|c| {
char_byte_counter += c.len_utf8();
return start_bytes > char_byte_counter;
})
.take_while(|c| *c != '<')
.collect::<String>();
Are there simpler ways to create substrings? Is there any part of the standard library I did not find?
I don't remember a built-in library function in other languages that works exactly the way you want (give me the substring between two patterns, or between the first and the end if the second does not exist).
I think you would have to write some custom logic anyway.
The closest equivalent to a "substring" function is slicing. However (as you found out) it works with bytes, not with unicode characters, so you will have to be careful with indices. In "Löwe", the 'e' is at (byte) index 4, not 3 (playground). But you can still use it in your case, because you are not working with indices directly (using find instead to... find the index you need for you)
Here's how you could do it with slicing (bonus, you don't need to re-allocate other Strings):
// adding some unicode to check that everything works
// also ouside of ASCII
let line = "asdfapatterndf1老虎23<12";
let start_bytes = line.find("pattern").unwrap_or(0); //index where "pattern" starts
// or beginning of line if
// "pattern" not found
let end_bytes = line.find("<").unwrap_or(line.len()); //index where "<" is found
// or end of line
let result = &line[start_bytes..end_bytes]; //slicing line, returns patterndf1老虎23
Try using something like the following method:
//Return result in &str or empty &str if not found
fn between<'a>(source: &'a str, start: &'a str, end: &'a str) -> &'a str {
let start_position = source.find(start);
if start_position.is_some() {
let start_position = start_position.unwrap() + start.len();
let source = &source[start_position..];
let end_position = source.find(end).unwrap_or_default();
return &source[..end_position];
}
return "";
}
This method approximate to O(n) with char and grapheme in mind. It works, but I'm not sure if there are any bugs.
fn between(str: &String, start: String, end: String, limit_one:bool, ignore_case: bool) -> Vec<String> {
let mut result:Vec<String> = vec![];
let mut starts = start.graphemes(true);
let mut ends = end.graphemes(true);
let sc = start.graphemes(true).count();
let ec = end.graphemes(true).count();
let mut m = 0;
let mut started:bool = false;
let mut temp = String::from("");
let mut temp2 = String::from("");
for c in str.graphemes(true) {
if started == false {
let opt = starts.next();
match opt {
Some(d) => {
if (ignore_case && c.to_uppercase().cmp(&d.to_uppercase()) == std::cmp::Ordering::Equal) || c == d {
m += 1;
if m == sc {
started = true;
starts = start.graphemes(true);
}
} else {
m = 0;
starts = start.graphemes(true);
}
},
None => {
starts = start.graphemes(true);
let opt = starts.next();
match opt {
Some(e) => {
if (ignore_case && c.to_uppercase().cmp(&e.to_uppercase()) == std::cmp::Ordering::Equal) || c == e {
m += 1;
if m == sc {
started = true;
starts = start.graphemes(true);
}
}
},
None => {}
}
}
}
}
else if started == true {
let opt = ends.next();
match opt {
Some(e) => {
if (ignore_case && c.to_uppercase().cmp(&e.to_uppercase()) == std::cmp::Ordering::Equal) || c == e {
m += 1;
temp2.push_str(e);
}
else {
temp.push_str(&temp2.to_string());
temp2 = String::from("") ;
temp.push_str(c);
ends = end.graphemes(true);
}
},
None => {
ends = end.graphemes(true);
let opt = ends.next();
match opt {
Some(e) => {
if (ignore_case && c.to_uppercase().cmp(&e.to_uppercase()) == std::cmp::Ordering::Equal) || c == e {
m += 1;
temp2.push_str(e);
}
else {
temp.push_str(&temp2.to_string());
temp2 = String::from("") ;
temp.push_str(c);
ends = end.graphemes(true);
}
},
None => {
}
}
}
}
if temp2.graphemes(true).count() == end.graphemes(true).count() {
temp2 = String::from("") ;
result.push(temp);
if limit_one == true { return result; }
started = false;
temp = String::from("") ;
}
}
}
return result;
}

How do I reduce the nesting of my code when dealing with nested Option types?

I'm new to Rust and I'd like to reduce the nesting of my code. Take this C# code as an example:
for (int i = 0; i < 100; i++)
{
var obj = arr[i];
if (obj != null)
{
var something = obj.Something;
if (something == null)
{
if (i % 3 == 0)
Console.WriteLine(i);
break;
}
}
}
ReSharper suggests rewriting it to reduce nesting:
for (int i = 0; i < 100; i++)
{
var obj = arr[i];
if (obj == null)
continue;
var something = obj.Something;
if (something != null)
continue;
if (i % 3 == 0)
Console.WriteLine(i);
break;
}
Is there way to do the same thing in Rust? If I have nested Option<Option<Option<T>>> and so on I should write something like:
fn main() {
for i in 1..100 {
if let Some(data) = some_data::get_some_data() {
if let Some(result) = data.some_work(i) {
if result > 80 {
break;
}
}
}
println!("{}", i);
}
}
mod some_data
{
pub struct SomeData {
value : i32
}
impl SomeData {
pub fn some_work(&self, i : i32) -> Option<i32> {
Some(self.value + i)
}
}
pub fn get_some_data() -> Option<SomeData> {
Some(SomeData { value : 50 })
}
}
this example is simplified, but it shows the core problem.
There are a few things you could do. One option would be to use a match and continue in the None arm, similar to your C# code:
fn main() {
for i in 1..100 {
let data = match some_data::get_some_data() {
None => continue,
Some(data) => data
};
let result = match data.some_work(i) {
None => continue,
Some(result) => result
};
if result > 80 {
break;
}
println!("{}", i);
}
}
Another thing you could do, as #Cecilio Pardo suggested, is use the is_then method of the Option type to chain your operations:
fn main() {
for i in 1..100 {
if let Some(result) = some_data::get_some_data()
.and_then(|data| data.some_work(i)) {
if result > 80 {
break;
}
}
}
}

Resources