Are there traditional style switch statements in Rust? - rust

I want to compare a value to constants, or other values. In another language (e.g JavaScript) I would do:
// Pretend these are values that are generated another way
let a = 23;
let b = 16;
let c = 43;
let input = 23;
switch (input) {
case a: console.log("Input is equal to a"); break;
case b: console.log("Input is equal to b"); break;
case c: console.log("Input is equal to c"); break;
default: console.log("Input does not equal any value"); break;
}
How would I do this neatly in Rust? I know I could do it with if statements, but I think this is messy and I am comparing to many more values.
Can I compare a variable to constant values in Rust using a match statement?

The simplest way to do this sort of case analysis is when you know the values of the cases beforehand and don't mind having them in the middle of the code. In this case, a simple match expression is what you want.
fn main() {
for &input in &[16, 23, 42, 43] {
match input {
23 => println!("Input is equal to a"),
16 => println!("Input is equal to b"),
43 => println!("Input is equal to c"),
_ => println!("Input does not equal any value"),
}
}
}
(playground link)
If your a b and c are compile-time constants (either a known value or generated with const functions), then you can still directly match on them.
const A: i32 = 23;
const B: i32 = 16;
const C: i32 = generate_c();
const fn generate_c() -> i32 {
A + B + 4
}
fn main() {
for &input in &[16, 23, 42, 43] {
match input {
A => println!("Input is equal to a"),
B => println!("Input is equal to b"),
C => println!("Input is equal to c"),
_ => println!("Input does not equal any value"),
}
}
}
(playground link)
However, if you try this with non-constant variables, you'll get weird outputs.
fn generate_c(a: i32, b: i32) -> i32 {
a + b + 4
}
fn main() {
let a = 23;
let b = 16;
let c = generate_c(a, b);
for &input in &[16, 23, 42, 43] {
match input {
a => println!("Input is equal to a"),
b => println!("Input is equal to b"),
c => println!("Input is equal to c"),
_ => println!("Input does not equal any value"),
}
}
}
(playground link)
If you run this, the compiler will give you lots of warnings about "unreachable patterns" and the output will be "Input is equal to a" all four times. The problem with this is that the left side of each line in a match statement is not simply an expression, but rather a pattern.
A pattern is an expression like (x, [_, z], Some(_)). It's built up from basic variables (like x and z), underscores (_), all the literal expressions (integers, floats, tuples, arrays) and a few other things.
When Rust runs a match statement like this, it tries to syntactically match the input with the pattern. Basic variables will match anything and the value of that variable is set to whatever it matched for the scope of that branch of the match statement. Underscores (as used in all of the above examples) match anything too, but don't bind any variables.
With the const version above, the constants A B and C are replaced with their respective literal values everywhere in the code, so the input is matched on those literal values.
With the variable version, when we match with a b and c, these letters are interpreted as basic variables that match anything. The values of the variables aren't considered at all in the pattern. In the code
let a = 14;
let b = 15;
let c = 16;
let input = 16;
match input {
a => println!("Input is equal to a"),
b => println!("Input is equal to b"),
c => println!("Input is equal to c"),
_ => println!("Input does not equal any value"),
}
the first branch will always match, giving the input the name a for the scope of the branch.
If you need to match on variables a b and c, you could add a guard to each branch. A guard filters the branch a little more by adding an additional condition for a branch to match. In this case, we match anything and bind it to the variable x, but then check that x is equal to a (and b and c).
fn generate_c(a: i32, b: i32) -> i32 {
a + b + 4
}
fn main() {
let a = 23;
let b = 16;
let c = generate_c(a, b);
for &input in &[16, 23, 42, 43] {
match input {
x if x == a => println!("Input is equal to a"),
x if x == b => println!("Input is equal to b"),
x if x == c => println!("Input is equal to c"),
_ => println!("Input does not equal any value"),
}
}
}
(playground link)
This is a little bit more verbose than the switch/case construction, but I hope it's clear what's going on. At each branch, the variable x is bound to 16, then if that's equal to the variable A (or B or C), then that branch is taken. Otherwise, we try the next branch.

Can I compare a variable to constant values in Rust using a match statement?
I wrote a macro to (almost) do this. It expands to a match with if guards.
switch! { input;
a => println!("Input is equal to a"),
b => println!("Input is equal to b"),
c => println!("Input is equal to c"),
_ => println!("Input does not equal any value"),
}

Related

How do I use a const range in a match statement? [duplicate]

This question already has an answer here:
How can I store a pattern in a variable in Rust?
(1 answer)
Closed 3 years ago.
This works:
match 0 {
0..=9 => (),
_ => (),
}
But this doesn't:
const R: std::ops::RangeInclusive<u8> = 0..=9;
fn main() {
match 0 {
R => (),
_ => (),
}
}
playground.
If I want to use a range with match, must I use a literal range?
I thinnk the problem is that when you match an u8, each match arm must offer values of type u8 that can be compared to the parameter.
Now, if you write 0..=9, this is not a RangeInclusive (even if it looks the same) - it is a range pattern that can be used to describe a range of values in a match arm.
So, if you write R => () in a match arm, the compiler complains (imo rightfully):
error[E0308]: mismatched types
--> src/main.rs:7:9
|
7 | R => (),
| ^ expected integer, found struct `std::ops::RangeInclusive`
|
= note: expected type `u8`
found type `std::ops::RangeInclusive<u8>`
I.e. it expects a u8 (or, implicitly, a range pattern for u8 values), but it found a RangeInclusive.
Now, one possible solution could be to define the lower and upper bound as separate constants:
const LOWER : u8 = 0;
const UPPER : u8 = 9;
const R: std::ops::RangeInclusive<u8> = LOWER..=UPPER; // <- this is a RangeInclusive<u8>
fn main() {
match 0 {
LOWER..=UPPER => (), // <- this is a range pattern
_ => (),
}
}
Another, in my opinion less appealing, would be a macro that just expands to your range:
macro_rules! myrng{() => {0..=9}}
const R: std::ops::RangeInclusive<u8> = myrng!();
fn main() {
match 0 {
myrng!() => (),
_ => (),
}
}

Writing a function to take iterables of reference and value type

I'd like to have a function that takes an iterable and returns its smallest and largest elements. This is part of an exercise in learning Rust, but I'm struggling in being able to handle reference types and value types at the same time.
This is what I have:
fn min_max<'a, I, T>(mut iter: I) -> Option<(&'a T, &'a T)>
where
I: Iterator<Item = &'a T>,
T: PartialOrd,
{
let mut min = match iter.next() {
Some(x) => x,
// The collection is empty
None => return None,
};
let mut max = min;
for el in iter {
if el < min {
min = el;
}
if el >= max {
max = el;
}
}
Some((min, max))
}
Then, I give this an iterator over some integers.
let nums: [u32; 6] = [4, 3, 9, 10, 4, 3];
if let Some((min, max)) = min_max(nums.iter()) {
println!("{} {}", min, max);
}
This works, and prints 3 10. But then I want to do some operations on the numbers before I compute the minimum and maximum, like a map and/or a filter.
let doubled = nums.iter().map(|x| 2 * x);
if let Some((min, max)) = min_max(doubled) {
println!("{} {}", min, max);
}
This gives a compiler error:
error[E0271]: type mismatch resolving `<[closure#src/main.rs:31:35: 31:44] as std::ops::FnOnce<(&u32,)>>::Output == &_`
--> src/main.rs:32:31
|
32 | if let Some((min, max)) = min_max(doubled) {
| ^^^^^^^ expected u32, found reference
|
= note: expected type `u32`
found type `&_`
= note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Map<std::slice::Iter<'_, u32>, [closure#src/main.rs:31:35: 31:44]>`
= note: required by `min_max`
This confused me, because if nums.iter() works as an argument, why shouldn't nums.iter().map(...)?
I understand the error message in principle: my array is of u32, not &u32, whereas my function requires Iterator::Item to be of type &'a T. But then I don't get why it errors only on the second sample (using .iter().map()) and not on the first (just .iter()).
I've made a playground with this example and a commented out example where I construct an iterable of integers from a string. This fails in exactly the same way as the second example above (and is closer to my actual use case).
let s = "4 3 9 10 4 3";
let parsed = s.split(" ").map(|x| x.parse::<u32>().unwrap());
if let Some((min, max)) = min_max(parsed) {
println!("{} {}", min, max);
}
I'd like to have a function that takes an iterable and returns its smallest and largest elements.
Use Itertools::minmax.
handle reference types and value types at the same time.
You don't need to — references to numbers can also be compared:
fn foo(a: &i32, b: &i32) -> bool {
a < b
}
In your case, remember that a value and a reference to that value are different types. That means you can accept an iterator of any type so long as the yielded values are comparable, and this includes both references and values, as requested:
fn min_max<I>(mut iter: I) -> Option<(I::Item, I::Item)>
where
I: Iterator,
I::Item: Clone + PartialOrd,
{
let mut min = match iter.next() {
Some(x) => x,
// The collection is empty
None => return None,
};
let mut max = min.clone();
for el in iter {
if el < min {
min = el;
} else if el >= max {
max = el;
}
}
Some((min, max))
}
I chose to add the Clone bound although to be more true to your original I could have used the Copy bound. Itertools returns an enum to avoid placing any restrictions on being able to duplicate the value.
This works with all three of your examples:
fn main() {
let nums: [u32; 6] = [4, 3, 9, 10, 4, 3];
if let Some((min, max)) = min_max(nums.iter()) {
println!("{} {}", min, max);
}
let doubled = nums.iter().map(|x| 2 * x);
if let Some((min, max)) = min_max(doubled) {
println!("{} {}", min, max);
}
let s = "4 3 9 10 4 3";
let parsed = s.split(" ").map(|x| x.parse::<u32>().unwrap());
if let Some((min, max)) = min_max(parsed) {
println!("{} {}", min, max);
}
}
3 10
6 20
3 10
my array is of u32, not &u32, whereas my function requires Iterator::Item to be of type &'a T. But then I don't get why it errors only on the second sample (using .iter().map()) and not on the first (just .iter()).
Because iterating over an array returns references. By using map, you are changing the type of the iterator's item from &i32 to i32. You could have also chosen to adapt the first call to return values.
You have a type mismatch problem because the .iter() call produces a "slice" iterator (Iterator with Item = &T), but the .map(|x| 2 * x) is a iterator adaptor, the call of which produces a new "value" iterator (Iterator with Item = T). These values must necessarily be stored in memory before we can get them "slice", because we can only get a reference to the value that is already stored somewhere in the memory. Therefore, we need to collect the result of the map function before we can get an iterator with references to the values it returns:
let doubled: Vec<_> = nums.iter().map(|x| 2 * x).collect();
if let Some((min, max)) = min_max(doubled.iter()) {
println!("{} {}", min, max);
}
For more details, see chapter 13.2 Iterators of The Rust Programming Language book.

Normalise ASCII numbers to digit numbers

Running example on play.rust-lang.org
fn main() {
show({
let number = b"123456";
for sequence in number.windows(6) {
let product = sequence.iter().fold(1, |a, &b| a * (b as u64));
println!("product of {:?} is {}", sequence, product);
}
});
}
Instead of having an output like "product of [49, 50, 51, 52, 53, 54] is 15312500000" I need the normal numbers in the brackets and the normalized result for the product.
Trying around with - b'0' to subtract the 48 to get the normal digits in line 5 doesn't work, i.e.
a * ((b as u64) -b'0')
or
(a - b'0') * (b as u64)
Seems I'm missing something here, for example I have no idea what exactly are the 'a' and 'b' values in the fold(). Can anyone enlighten me? :)
Looking at the signature of fold, we can see that it takes two arguments:
fn fold<B, F>(self, init: B, f: F) -> B
where F: FnMut(B, Self::Item) -> B
init, which is of some arbitrary type B, and f, which is a closure that takes a B value and an element from the iterator, in order to compute a new B value. The whole function returns a B. The types are strongly suggestive of what happens: the closure f is repeatedly called on successive elements of the iterator, passing the computed B value into the next f call. Checking the implementation confirms this suspicion:
let mut accum = init;
for x in self {
accum = f(accum, x);
}
accum
It runs through the iterator, passing the accumulated state into the closure in order to compute the next state.
First things first, lets put the type on the fold call:
let product = sequence.iter().fold(1, |a: u64, &b: &u8| a * (b as u64));
That is, the B type we want is u64 (that's what our final product will be), and the item type of the iterator is &u8, a reference to a byte.
Now, we can manually inline the definition of fold to compute product to try to clarify the desired behaviour (I'm ignoring the normalisation for now):
let mut accum = 1;
for x in sequence.iter() {
accum = { // the closure
let a: u64 = accum;
let &b: &u8 = x;
a * b as u64
}
}
let product = accum;
Simplifying:
let mut product = 1;
for &b in sequence.iter() {
product = product * (b as u64)
}
Hopefully this makes it clearer what needs to happen: b runs across each byte, and so it is the value that needs adjustment, to bring the ASCII encoded value down to the expected 0..10 range.
So, you were right with:
a * ((b as u64) -b'0')
However, the details mean that fails to compile, with a type error: b'0' has type u8, but b as u64 as type u64, and it's not legal to use - with u64 and u8. Moving the normalisation to happen before the u64 cast will ensure this works ok, since then you're subtracting b (which is a u8) and a u8:
product * (b - b'0') as u64
All in all, the fold might look clearer (and actually work) as:
let product = sequence.iter()
.fold(1, |prod, &byte| prod * (byte - b'0') as u64);
(I apologise for giving you such confusing code on IRC.)
As an alternative to fold, you can use map and MultiplicativeIterator::product. I find that the two steps help make it clearer what is happening.
#![feature(core)]
use std::iter::MultiplicativeIterator;
fn main() {
let number = b"123456";
for sequence in number.windows(6) {
let product = sequence.iter().map(|v| (v - b'0') as u64).product();
println!("product of {:?} is {}", sequence, product);
}
}
You could even choose to split up the resizing from u8 to u64:
sequence.iter().map(|v| v - b'0').map(|v| v as u64).product();
Nowadays, an alternative is product + to_digit: (itertools was used to print the contents of the iterator)
use {itertools::Itertools, std::char};
fn main() {
let number = b"123456";
let sequence = number
.iter()
.map(|&c| u64::from(char::from(c).to_digit(10).expect("not a digit")));
let product: u64 = sequence.clone().product();
println!("product of {:?} is {}", sequence.format(", "), product);
}
(playground)

Rust compound declaration

In C you can delcare a bunch of similar variables at once:
int a=1, b=2, c=3;
How would you do this in rust? I can do something like:
let (mut a, mut b, mut c) = (1i, 2i, 3i);
But this requires stating mut and i multiple times. Is there a shorter way to do this?
There isn't a shorter way to do this.
Well, that's not quite true. You could define a macro:
#![feature(macro_rules)]
macro_rules! multi_mut_let {
( $( $id: ident = $value: expr ),* ) => {
let ( $( mut $id, )* ) = ($( $value, )*);
}
}
fn main() {
multi_mut_let!(a = 1i, b = 2u, c = 3f64);
a += 1;
b *= 2;
c -= 3.0;
println!("{} {} {}", a, b, c); // 2 4 0
// edge cases (which are handled fine):
multi_mut_let!();
multi_mut_let!(_x = 2u);
}
Those with sharp eyes will notice the comma is placed slightly strangely in the RHS of the macro expansion, since it results in a trailing comma on both sides of the let. This allows the second edge case to be handled correctly. Without the trailing comma, it expands to let (mut _x) = (2u);, but parens like that are not (yet) allowed in patterns; with the trailing comma it expands to let (mut _x,) = (2u,);, which are 1-tuples, and so the pattern matching is fine.

Is it possible to specify the lifetime on match variables in rust?

For example:
enum FooBar {
Bar(Vec<int>),
Other
}
fn main() {
let default:&[int] = [];
let x = Bar(Vec::from_slice([1, 2, 3]));
let y = match(x) {
Bar(z) => z.slice(0, z.len()),
_ => default
};
println!("{}", y);
}
In this example the slice is not valid because of the lifetime on z; but we know that the actual data here (x) is valid for the entire block.
Is there some 'a syntax you can use here to help hint this to compiler? If so, how do you do it?
(I believe I've seen some kind of 'a match { } or match x 'a { } / something syntax in the past, but I can't find any examples now that I'm looking)
Sure, there is some syntax, though it is not as explicit on lifetimes as you mean and in fact it is more general. By doing match as you do you are actually moving x into the match, so it is consumed. No wonder that z.slice(...) can't escape the block. But you can avoid moving x into the match by using reference patterns (note the ref before z):
let default: &[int] = [];
let x = Bar(vec![1, 2, 3]);
let y = match x {
Bar(ref z) => z.slice(0, z.len()),
Other => default
};
println!("{}", y);
This way x is not moved into match block - its internals are borrowed instead. This program compiles and prints out [1, 2, 3].

Resources