I am trying to do a simple quadratic function that would return number of roots and their values via an enum:
enum QuadraticResult {
None,
OneRoot(f32),
TwoRoots(f32, f32),
}
fn solveQuadratic(a: f32, b: f32, c: f32) -> QuadraticResult {
let delta = b * b - 4.0 * a * c;
match delta {
< 0 => return QuadraticResult::None,
> 0 => return QuadraticResult::TwoRoots(0.0, 1.0),
_ => return QuadraticResult::OneRoot(0.0),
}
}
This doesn't compile as it complains about '<' and '>'. Is there a way to achieve this with match or do I need to use if
You can use a match guard, but that feels more verbose than a plain if statement:
return match delta {
d if d < 0 => QuadraticResult::None,
d if d > 0 => QuadraticResult::TwoRoots(0.0, 1.0),
_ => QuadraticResult::OneRoot(0.0),
}
If you want to handle the three cases where some value is greater than, equal to or less than another, you can match on an Ordering, which you can obtain by calling cmp (from the Ord trait) or partial_cmp (from the PartialOrd trait).
fn solve_quadratic(a: f32, b: f32, c: f32) -> QuadraticResult {
let delta = b * b - 4.0 * a * c;
match delta.partial_cmp(&0.0).expect("I don't like NaNs") {
Ordering::Less => QuadraticResult::None,
Ordering::Greater => QuadraticResult::TwoRoots(0.0, 1.0),
Ordering::Equal => QuadraticResult::OneRoot(0.0),
}
}
You can, but you'll want to create a variable binding when you do it and turn it into an actual expression:
match delta {
d if d < 0.0 => QuadraticResult::None,
d if d > 0.0 => QuadraticResult::TwoRoots(0.0, 1.0),
_ => QuadraticResult::OneRoot(0.0),
}
I'm not sure this is any better than just splitting this into an if statement though.
Warning: as of rustc 1.67.0 (fc594f156 2023-01-24) this method still works, but will stop working for float literals in a future release.
You can use a Range Pattern which are supported in match expressions. This requires the exclusive_range_pattern feature.
#![feature(exclusive_range_pattern)]
fn solveQuadratic(a: f32, b: f32, c: f32) -> QuadraticResult {
let delta = b * b - 4.0 * a * c;
match delta {
std::f32::MIN..0.0 => return QuadraticResult::None,
0.0 => return QuadraticResult::OneRoot(0.0),
_ => return QuadraticResult::TwoRoots(0.0, 1.0),
}
}
Playground
Related
I am trying to do a simple quadratic function that would return number of roots and their values via an enum:
enum QuadraticResult {
None,
OneRoot(f32),
TwoRoots(f32, f32),
}
fn solveQuadratic(a: f32, b: f32, c: f32) -> QuadraticResult {
let delta = b * b - 4.0 * a * c;
match delta {
< 0 => return QuadraticResult::None,
> 0 => return QuadraticResult::TwoRoots(0.0, 1.0),
_ => return QuadraticResult::OneRoot(0.0),
}
}
This doesn't compile as it complains about '<' and '>'. Is there a way to achieve this with match or do I need to use if
You can use a match guard, but that feels more verbose than a plain if statement:
return match delta {
d if d < 0 => QuadraticResult::None,
d if d > 0 => QuadraticResult::TwoRoots(0.0, 1.0),
_ => QuadraticResult::OneRoot(0.0),
}
If you want to handle the three cases where some value is greater than, equal to or less than another, you can match on an Ordering, which you can obtain by calling cmp (from the Ord trait) or partial_cmp (from the PartialOrd trait).
fn solve_quadratic(a: f32, b: f32, c: f32) -> QuadraticResult {
let delta = b * b - 4.0 * a * c;
match delta.partial_cmp(&0.0).expect("I don't like NaNs") {
Ordering::Less => QuadraticResult::None,
Ordering::Greater => QuadraticResult::TwoRoots(0.0, 1.0),
Ordering::Equal => QuadraticResult::OneRoot(0.0),
}
}
You can, but you'll want to create a variable binding when you do it and turn it into an actual expression:
match delta {
d if d < 0.0 => QuadraticResult::None,
d if d > 0.0 => QuadraticResult::TwoRoots(0.0, 1.0),
_ => QuadraticResult::OneRoot(0.0),
}
I'm not sure this is any better than just splitting this into an if statement though.
Warning: as of rustc 1.67.0 (fc594f156 2023-01-24) this method still works, but will stop working for float literals in a future release.
You can use a Range Pattern which are supported in match expressions. This requires the exclusive_range_pattern feature.
#![feature(exclusive_range_pattern)]
fn solveQuadratic(a: f32, b: f32, c: f32) -> QuadraticResult {
let delta = b * b - 4.0 * a * c;
match delta {
std::f32::MIN..0.0 => return QuadraticResult::None,
0.0 => return QuadraticResult::OneRoot(0.0),
_ => return QuadraticResult::TwoRoots(0.0, 1.0),
}
}
Playground
The following two functions generate very different resulting assembly language:
pub struct X {
a: u64,
b: u64,
c: u64,
d: u64,
e: u64,
f: u64,
}
pub fn f(a: u8, x: X) -> u64 {
[
(0b000001, x.a),
(0b000010, x.b),
(0b000100, x.c),
(0b001000, x.d),
(0b010000, x.e),
(0b100000, x.f),
]
.into_iter()
.find(|(bb, _)| (*bb & a) != 0)
.map_or(0, |(_, m)| m)
}
pub fn g(a: u8, x: X) -> u64 {
match a {
_ if (a & 0b000001) != 0 => x.a,
_ if (a & 0b000010) != 0 => x.b,
_ if (a & 0b000100) != 0 => x.c,
_ if (a & 0b001000) != 0 => x.d,
_ if (a & 0b010000) != 0 => x.e,
_ if (a & 0b100000) != 0 => x.f,
_ => 0,
}
}
They do the same thing: based on a bit pattern, return the proper value. I prefer f because it separates the data and the logic, but it results in inferior assembly. Because I'm running simulations, a little is a lot. (see assembly with above playground link, generate release asm)
In f, Rust unnecessarily builds the array in memory instead of recognizing that the values are used and immediately discarded. g mashes the data and logic together, but Rust simply does the comparisons then returns the results, as you would expect.
Is there anything I can do to help this iterator-style code generate better code or am I better off writing imperative style?
There's also the slightly shorter
pub fn h(a: u8, x: X) -> u64 {
match a.trailing_zeros() {
0 => x.a,
1 => x.b,
2 => x.c,
3 => x.d,
4 => x.e,
5 => x.f,
_ => 0
}
}
Not sure how its assembly looks.
As a Rust beginner working on one of the first problems on Exercism/Rust (https://exercism.org/tracks/rust/exercises/assembly-line)
I would like to know if it is possible to constrain integer input to a range at compile-time
to be able to have a clean set of match expression cases.
Below is my current implementation of production_rate_per_hour:
pub fn production_rate_per_hour(mut speed: u8) -> f64 {
speed = cmp::max(speed, 10);
let cars_per_hour: u8 = 221;
match speed {
0 => 0.0,
1 ..= 4 => (speed * cars_per_hour) as f64,
5 ..= 8 => (speed * cars_per_hour) as f64 * 0.9,
9 | 10 => (speed * cars_per_hour) as f64 * 0.77
}
}
I am trying to write a method that accepts a single mutable u8 argument named speed that I then constrain to the range 0..=10 as follows:
speed = cmp::max(speed, 10);
I then want to match speed on all possible cases, i.e. 0..=10.
But since this is a run-time check, the compiler does not see this and tells me to also match integer value 11 and higher:
Compiling assembly-line v0.1.0 (/Users/michahell/Exercism/rust/assembly-line)
error[E0004]: non-exhaustive patterns: `11_u8..=u8::MAX` not covered
--> src/lib.rs:12:11
|
12 | match speed {
| ^^^^^ pattern `11_u8..=u8::MAX` not covered
|
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
= note: the matched value is of type `u8`
I can of course solve this by adding the following case:
// notify
_ => println!("11 or higher")
// or crash
_ => panic!("you've crashed the assembly line!");
// or do something like this:
_ => (cmp::max(speed, 10) * cars_per_hour) as f64 * 0.77;
However, I would like to know if it is possible to constrain the input range at compile-time, and having a "clean" match expression.
Is this possible, if so, how?
There is currently no way to express this in the type system.
I assume you mean min instead of max. The typical approach would be:
pub fn production_rate_per_hour(mut speed: u8) -> f64 {
speed = cmp::min(speed, 10);
let cars_per_hour: u8 = 221;
match speed {
0 => 0.0,
1 ..= 4 => (speed * cars_per_hour) as f64,
5 ..= 8 => (speed * cars_per_hour) as f64 * 0.9,
9 | 10 => (speed * cars_per_hour) as f64 * 0.77,
_ => unreachable!(),
}
}
A bug in your program (e.g., accidentally raising the cap to 11) will result in a panic. If this is performance sensitive, you can use unsafe:
_ => unsafe { std::hint::unreachable_unchecked() },
If your claim that this branch is unreachable is false, you get undefined behavior.
Note that in many cases, the compiler will be able to prove the unreachable branch is, in fact, unreachable and elide it completely. This is very common for e.g. modular arithmetic:
Example
pub fn foo(v: u64) -> u8 {
match v % 8 {
0 => 0,
1 ..= 4 => 1,
5 ..= 7 => 2,
_ => unreachable!(),
}
}
Note that after a slight refactoring:
pub fn production_rate_per_hour(speed: u8) -> f64 {
let speed = speed.min(10);
let factor = match speed {
0 => 0.0_f64,
1 ..= 4 => 1.0,
5 ..= 8 => 0.9,
9.. => 0.77,
};
let cars_per_hour: u8 = 221;
factor * (speed * cars_per_hour) as f64
}
There is no case where unreachable is needed. The tradeoff is you no longer get to be very explicit in the match about what values of speed are acceptable. Whether this or a panic is better depends on your context.
I have an issue with the following Rust code:
pub fn median(v: &Vec<i32>) -> f32 {
let len = v.len();
match len % 2 {
0 => (v[len / 2 - 1] + v[len / 2]) as f32 / 2 as f32,
1 => v[(len - 1) / 2] as f32,
}
}
This code doesn't compile due to a 'Non exhaustive patterns' error.
Why is that? What does the % operator return?
The compiler is not smart enough to figure out that the result of len % 2 can only ever be 0 or 1. It demands a match arm for cases where the result is some other value. You can solve this by explicitly saying that those cases are impossible:
match len % 2 {
0 => (v[len / 2 - 1] + v[len / 2]) as f32 / 2 as f32,
1 => v[(len - 1) / 2] as f32,
_ => unreachable!()
}
The _ will match any other value not previously mentioned. The unreachable!() tells the compiler "this code will never execute", but cause a panic!() just in case it does in fact execute. That way, the program is correct all the time at practically no cost.
Future versions of the compiler might figure out that the values 2.. or not possible.
The % is the remainder operator (not to be cofused with the mod-operator).
The simplest fix is to use _ instead of 1:
match len % 2 {
0 => (v[len / 2 - 1] + v[len / 2]) as f32 / 2 as f32,
_ => v[(len - 1) / 2] as f32,
}
Because len is odd and non-negative in the second branch, (len - 1) / 2 is the same as len / 2. I would simplify it like this:
let mid = v.len() / 2;
match v.len() % 2 {
0 => (v[mid - 1] + v[mid]) as f32 / 2.0,
_ => v[mid] as f32,
}
In Clojure, I use a function called iterate that:
Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free of side-effects
Is there something similar in Rust?
For example, I have this function that I'd like to pass to a function with a number and then iterate over a stream/range/vector until I find Some(1):
fn coltz(n: u64) -> Option<u64> {
match n % 2 {
0 => Some(n / 2),
_ => Some(3 * n + 1)
}
}
You can repeatedly call a closure using std::iter::repeat_with():
repeat_with(move || {
let result = x;
x = f(x);
result
})
The return value is an iterator over the successive return values of the closure.
We use move to move x into the closure, as the current state of our iteration. Inside the closure we update x with f(x) and return the old value (so in the first iteration we return the original x).
Here is a complete working example:
use std::iter::repeat_with;
fn collatz(n: u64) -> u64 {
match n % 2 {
0 => n / 2,
_ => 3 * n + 1,
}
}
fn iterate<F, X>(f: F, mut x: X) -> impl Iterator<Item = X>
where
F: Fn(X) -> X,
X: Copy,
{
repeat_with(move || {
let result = x;
x = f(x);
result
})
}
fn main() {
for i in iterate(collatz, 12).take_while(|&x| x != 1) {
println!("{}", i);
}
}
Playground
As of Rust 1.34, you can use iter::successors:
fn coltz(n: u64) -> Option<u64> {
match n % 2 {
0 => Some(n / 2),
_ => Some(3 * n + 1),
}
}
use std::iter;
fn main() {
let sequence = iter::successors(Some(10), |&v| coltz(v)).take_while(|&v| v != 1);
for v in sequence {
println!("{}", v);
}
}
12
6
3
10
5
16
8
4
2