How to ensure my function will receive a Vec of valid length? - rust

Is it possible to specify that a function's Vec argument has a certain length? Consider the possible values of a dice:
fn new(d_type: DiceType, face_vals: /*Vec<u32> with len() == 2/4/6/8/10/12/20*/) -> Dice {...}
I am writing something that lets you create a polyhedral dice (usual RPG sizes: 2, 4, 6, etc.) with specified face values. I remember that when you call a Rust function without the unsafe keyword the users should be able to call it however they like without fear of failure so simply checking for validity in the function and returning some "you messed up" error is bad Rust.
How can I achieve this?
This is a part of the code I am working on:
pub enum DiceType {
D2,
D4,
D6,
D8,
D10,
D10P,
D12,
D20,
}
pub struct Dice {
dice_type: DiceType,
face_count: usize,
face_values: Vec<u32>,
}
impl Dice {
pub fn new(d_type: DiceType, face_vals: Vec<u32>) -> Dice {
let mut retval;
//Reject if not a valid dice type 2, 4, 6, 8, 10, 12, or 20
//I really shouldn't be doing this should I?
if Dice::valid_dice(d_type, face_vals) {
retval = Dice {
dice_type: d_type,
face_count: face_vals.len(),
face_values: face_vals,
}
} else {
//User wont know they got an error
//Really shouldn't need to go here. How do I avoid needing
//error checking?
retval = Dice {
dice_type: None,
face_count: 2,
face_values: face_vals,
};
}
retval
}
}
Answer
The accepted answer shows a good use of results to return a value but the response got me thinking on how to make the code more flexible while still having a hard cap that could guarantee overflow safety for a single roll so I cut out a bunch of code and came up with the following which should let you generate any dice roll between 1-10,000 per roll with multipliers for extra rolls.
const MAX_FACE_VALUE: u32 = 100000;
const MAX_FACE_COUNT: u32 = 10000;
const MAX_ROLL_COUNT: u32 = 9999;
pub struct Dice {
face_count: usize,
face_values: Vec<u32>,
}
impl Dice {
pub fn new(mut face_vals: Vec<u32>) -> Self {
//User may not have values greater than 100,000
//Index access is safe since we use the for _ in _
for x in 0..face_vals.len() {
if face_vals[x] > MAX_FACE_VALUE {
//Enforce the limit
face_vals[x] = MAX_FACE_VALUE;
}
}
//User may not have more than 10,000 faces
if face_vals.len() > MAX_FACE_COUNT as usize {
let new_vals: Vec<u32> = face_vals.split_off(MAX_FACE_COUNT as usize);
Dice {
face_count: MAX_FACE_COUNT as usize,
face_values: new_vals,
}
} else if face_vals.len() == 0 {
//No 0 sided dice allowed
Dice {
face_count: 1,
face_values: vec![1],
}
} else {
//Normal range
Dice {
face_count: face_vals.len(),
face_values: face_vals,
}
}
}
}

You should use an enum that has variants with corresponding arrays of a fixed length:
#[derive(Clone, Copy)]
pub enum Dice {
D2([u32; 2]),
D4([u32; 4]),
D6([u32; 6]),
D8([u32; 8]),
D10([u32; 10]),
D10P([u32; 10]),
D12([u32; 12]),
D20([u32; 20]),
}
Then you cannot have invalid value:
fn take_a_dice(_dice: Dice) {
//
}
fn main() {
take_a_dice(Dice::D4([1, 2, 4, 8]));
}

You should use a Result to be able to account for possible erroneous inputs:
use std::cmp::Ordering;
#[derive(Clone, Copy)]
pub enum DiceType {
D2,
D4,
D6,
D8,
D10,
D10P,
D12,
D20
}
pub struct Dice {
dice_type: DiceType,
// no need for face_count, it's a method of DiceType
face_values: Vec<u32>
}
// an error for invalid face value inputs
enum DiceError {
TooFewFaceValues,
TooManyFaceValues
}
impl DiceType {
fn face_count(&self) -> usize {
match self {
DiceType::D2 => 2,
DiceType::D4 => 4,
_ => unimplemented!() // TODO: account for all the other variants
}
}
}
impl Dice {
fn new(dice_type: DiceType, face_values: &[u32]) -> Result<Self, DiceError> {
match face_values.len().cmp(&dice_type.face_count()) {
Ordering::Less => Err(DiceError::TooFewFaceValues),
Ordering::Greater => Err(DiceError::TooManyFaceValues),
Ordering::Equal => Ok(
Dice {
dice_type,
face_values: Vec::from(face_values)
}
)
}
}
}

Related

Rust equivalent of C++ union with anonymous structs

Is there a Rust equivalent of the following C++ sample (that I've written for this question):
union example {
uint32_t fullValue;
struct {
unsigned sixteen1: 16;
unsigned sixteen2: 16;
};
struct {
unsigned three: 3;
unsigned twentynine: 29;
};
};
example e;
e.fullValue = 12345678;
std::cout << e.sixteen1 << ' ' << e.sixteen2 << ' ' << e.three << ' ' << e.twentynine;
For reference, I'm writing a CPU emulator & easily being able to split out binary parts of a variable like this & reference them by different names, makes the code much simpler. I know how to do this in C++ (as above), but am struggling to work out how to do the equivalent in Rust.
You could do this by creating a newtype struct and extracting the relevant bits using masking and/or shifts.
This code to do this is slightly longer (but not much so) and importantly avoids the undefined behavior you are triggering in C++.
#[derive(Debug, Clone, Copy)]
struct Example(pub u32);
impl Example {
pub fn sixteen1(self) -> u32 {
self.0 & 0xffff
}
pub fn sixteen2(self) -> u32 {
self.0 >> 16
}
pub fn three(self) -> u32 {
self.0 & 7
}
pub fn twentynine(self) -> u32 {
self.0 >> 3
}
}
pub fn main() {
let e = Example(12345678);
println!("{} {} {} {}", e.sixteen1(), e.sixteen2(), e.three(), e.twentynine());
}
Update
You can make some macros for extracting certain bits:
// Create a u32 mask that's all 0 except for one patch of 1's that
// begins at index `start` and continues for `len` digits.
macro_rules! mask {
($start:expr, $len:expr) => {
{
assert!($start >= 0);
assert!($len > 0);
assert!($start + $len <= 32);
if $len == 32 {
assert!($start == 0);
0xffffffffu32
} else {
((1u32 << $len) - 1) << $start
}
}
}
}
const _: () = assert!(mask!(3, 7) == 0b1111111000);
const _: () = assert!(mask!(0, 32) == 0xffffffff);
// Select `num_bits` bits from `value` starting at `start`.
// For example, select_bits!(0xabcd1234, 8, 12) == 0xd12
// because the created mask is 0x000fff00.
macro_rules! select_bits {
($value:expr, $start:expr, $num_bits:expr) => {
{
let mask = mask!($start, $num_bits);
($value & mask) >> mask.trailing_zeros()
}
}
}
const _: () = assert!(select_bits!(0xabcd1234, 8, 12) == 0xd12);
Then either use these directly on a u32 or make a struct to implement taking certain bits:
struct Example {
v: u32,
}
impl Example {
pub fn first_16(&self) -> u32 {
select_bits!(self.v, 0, 16)
}
pub fn last_16(&self) -> u32 {
select_bits!(self.v, 16, 16)
}
pub fn first_3(&self) -> u32 {
select_bits!(self.v, 0, 3)
}
pub fn last_29(&self) -> u32 {
select_bits!(self.v, 3, 29)
}
}
fn main() {
// Use hex for more easily checking the expected values.
let e = Example { v: 0x12345678 };
println!("{:x} {:x} {:x} {:x}", e.first_16(), e.last_16(), e.first_3(), e.last_29());
// Or use decimal for checking with the provided C code.
let e = Example { v: 12345678 };
println!("{} {} {} {}", e.first_16(), e.last_16(), e.first_3(), e.last_29());
}
Original Answer
While Rust does have unions, it may be better to use a struct for your use case and just get bits from the struct's single value.
// Create a u32 mask that's all 0 except for one patch of 1's that
// begins at index `start` and continues for `len` digits.
macro_rules! mask {
($start:expr, $len:expr) => {
{
assert!($start >= 0);
assert!($len > 0);
assert!($start + $len <= 32);
let mut mask = 0u32;
for i in 0..$len {
mask |= 1u32 << (i + $start);
}
mask
}
}
}
struct Example {
v: u32,
}
impl Example {
pub fn first_16(&self) -> u32 {
self.get_bits(mask!(0, 16))
}
pub fn last_16(&self) -> u32 {
self.get_bits(mask!(16, 16))
}
pub fn first_3(&self) -> u32 {
self.get_bits(mask!(0, 3))
}
pub fn last_29(&self) -> u32 {
self.get_bits(mask!(3, 29))
}
// Get the bits of `self.v` specified by `mask`.
// Example:
// self.v == 0xa9bf01f3
// mask == 0x00fff000
// The result is 0xbf0
fn get_bits(&self, mask: u32) -> u32 {
// Find how many trailing zeros `mask` (in binary) has.
// For example, the mask 0xa0 == 0b10100000 has 5.
let mut trailing_zeros_count_of_mask = 0;
while mask & (1u32 << trailing_zeros_count_of_mask) == 0 {
trailing_zeros_count_of_mask += 1;
}
(self.v & mask) >> trailing_zeros_count_of_mask
}
}
fn main() {
// Use hex for more easily checking the expected values.
let e = Example { v: 0x12345678 };
println!("{:x} {:x} {:x} {:x}", e.first_16(), e.last_16(), e.first_3(), e.last_29());
// Or use decimal for checking with the provided C code.
let e = Example { v: 12345678 };
println!("{} {} {} {}", e.first_16(), e.last_16(), e.first_3(), e.last_29());
}
This setup makes it easy to select any range of bits you want. For example, if you want to get the middle 16 bits of the u32, you just define:
pub fn middle_16(&self) -> u32 {
self.get_bits(mask!(8, 16))
}
And you don't even really need the struct. Instead of having get_bits() be a method, you could define it to take a u32 value and mask, and then define functions like
pub fn first_3(v: u32) -> u32 {
get_bits(v, mask!(0, 3))
}
Note
I think this Rust code works the same regardless of your machine's endianness, but I've only run it on my little-endian machine. You should double check it if it could be a problem for you.
You could use the bitfield crate.
This appears to approximate what you are looking for at least on a syntactic level.
For reference, your original C++ code prints:
24910 188 6 1543209
Now there is no built-in functionality in Rust for bitfields, but there is the bitfield crate.
It allows specifying a newtype struct and then generates setters/getters for parts of the wrapped value.
For example pub twentynine, set_twentynine: 31, 3; means that it should generate the setter set_twentynine() and getter twentynine() that sets/gets the bits 3 through 31, both included.
So transferring your C++ union into a Rust bitfield, this is how it could look like:
use bitfield::bitfield;
bitfield! {
pub struct Example (u32);
pub full_value, set_full_value: 31, 0;
pub sixteen1, set_sixteen1: 15, 0;
pub sixteen2, set_sixteen2: 31, 16;
pub three, set_three: 2, 0;
pub twentynine, set_twentynine: 31, 3;
}
fn main() {
let mut e = Example(0);
e.set_full_value(12345678);
println!(
"{} {} {} {}",
e.sixteen1(),
e.sixteen2(),
e.three(),
e.twentynine()
);
}
24910 188 6 1543209
Note that those generated setters/getters are small enough to have a very high chance to be inlined by the compiler, giving you zero overhead.
Of course if you want to avoid adding an additional dependency and instead want to implement the getters/setters by hand, look at #apilat's answer instead.
Alternative: the c2rust-bitfields crate:
use c2rust_bitfields::BitfieldStruct;
#[repr(C, align(1))]
#[derive(BitfieldStruct)]
struct Example {
#[bitfield(name = "full_value", ty = "u32", bits = "0..=31")]
#[bitfield(name = "sixteen1", ty = "u16", bits = "0..=15")]
#[bitfield(name = "sixteen2", ty = "u16", bits = "16..=31")]
#[bitfield(name = "three", ty = "u8", bits = "0..=2")]
#[bitfield(name = "twentynine", ty = "u32", bits = "3..=31")]
data: [u8; 4],
}
fn main() {
let mut e = Example { data: [0; 4] };
e.set_full_value(12345678);
println!(
"{} {} {} {}",
e.sixteen1(),
e.sixteen2(),
e.three(),
e.twentynine()
);
}
24910 188 6 1543209
Advantage of this one is that you can specify the type of the union parts yourself; the first one was u32 for all of them.
I'm unsure, however, how endianess plays into this one. It might yield different results on a system with different endianess. Might require further research to be sure.

the trait `PartialEq<Option<_>>` is not implemented

I have a struct where I've derived a couple of things.
#[derive(PartialEq, Debug)]
struct Subscriber {
id: u16,
up_speed: u32,
down_speed: u32
}
However, when I try to use PartialEq, I get told it is not implemented.
for (id, subscriber) in &new_hashmap {
let original_subscriber = original_hashmap.get(id).unwrap();
if original_subscriber == None {
changed_hashmap.insert(subscriber.id, subscriber);
} else if subscriber != original_subscriber {
changed_hashmap.insert(subscriber.id, subscriber);
}
}
Here's the compiler error.
error[E0277]: can't compare `&Subscriber` with `Option<_>`
--> src/main.rs:34:32
|
34 | if original_subscriber == None {
| ^^ no implementation for `&Subscriber == Option<_>`
|
= help: the trait `PartialEq<Option<_>>` is not implemented for `&Subscriber`
= help: the trait `PartialEq` is implemented for `Subscriber`
If I rewrite it to not put original_subscriber into its own variable, then it works.
for (id, subscriber) in &new_hashmap {
if original_hashmap.get(id) == None {
changed_hashmap.insert(subscriber.id, subscriber);
} else if subscriber != original_hashmap.get(id).unwrap() {
changed_hashmap.insert(subscriber.id, subscriber);
}
}
The rest of the code is essentially doing the following.
Create HashMap of 2 Subscriber instances.
Create another HashMap of 3 Subscriber instances, 1 of which is new, 1 of which is the same, and 1 of which has the same key but an updated value.
That is original_hashmap HashMap and new_hashmap.
The goal is to get a third HashMap of items in new_hashmap that are new to original_hashmap or have changed values.
your code does not work for 2 reasons.
If you derive PartialEq it will only work for Subscriber == Subscriber checks. You need to implement PartialEq<Type>
You are using a reference when comparing. This means you need to implement PartialEq for &Subscriber and not subscriber
This should do the trick
#[derive(PartialEq, Debug)]
struct Subscriber {
id: u16,
up_speed: u32,
down_speed: u32,
}
let subscriber = Subscriber {
id: 1,
up_speed: 100,
down_speed: 100,
};
impl PartialEq<Option<Subscriber>> for &Subscriber {
fn eq(&self, other: &Option<Subscriber>) -> bool {
match other {
Some(other) => return other == *self,
None => return false,
}
}
}
if &subscriber == None {
println!("None");
} else {
println!("Some");
}
But I am not sure if this is really what you want. I will try to implement the same and edit my answer afterwards
I suppose that's what you want to implement
use std::collections::HashMap;
#[derive(Debug, PartialEq)]
struct Subscriber {
id: u16,
up_speed: u32,
down_speed: u32,
}
impl Subscriber {
fn new(id: u16, up_speed: u32, down_speed: u32) -> Subscriber {
Subscriber {
id,
up_speed,
down_speed,
}
}
}
fn main() {
let mut old_map = HashMap::new();
old_map.insert(1, Subscriber::new(1, 1, 1));
old_map.insert(2, Subscriber::new(2, 2, 2));
let mut new_map = HashMap::new();
new_map.insert(0, Subscriber::new(0, 0, 0)); //new
new_map.insert(1, Subscriber::new(1, 1, 1)); //Same
new_map.insert(2, Subscriber::new(3, 3, 3)); //Same key but different value
let mut changed_map = HashMap::new();
//
for (key, subscriber) in &new_map {
if old_map.contains_key(&key) {
if old_map[&key] != *subscriber {
changed_map.insert(key, subscriber);
}
} else {
changed_map.insert(key, subscriber);
}
}
println!("{:?}", changed_map);
}
It will return
{2: Subscriber { id: 3, up_speed: 3, down_speed: 3 }, 0: Subscriber { id: 0, up_speed: 0, down_speed: 0 }}
I used the deref operator to avoid impl PartialEq<Subscriber> for &Subscriber but you could have done that as well

How do I use an impl function in the main function in rust?

I want to use a function in the main function in a rust program that I am building to help me learn rust and come up with an error: self value is a keyword only available in methods with a self parameterrustc(E0424). What can I fix in my code so that this error does not happen?
pub use crate::user_account::user_account;
use rand::Rng;
#[allow(dead_code)]
pub trait UserInfo {
fn user_info(&mut self);
fn acc_no(&mut self);
fn yes(self);
fn bank_new_user(self);
}
pub struct NewUser {
age: String,
new_user: String,
account: String,
account_number: i32,
routing_number: i32,
select: String,
}
impl UserInfo for NewUser {
fn user_info(&mut self) {
self.age = String::new();
self.new_user = String::new();
println!("What is your name?");
print!("Name: ");
std::io::stdin().read_line(&mut self.new_user);
println!(" ");
println!("Hello {}, What is your age? ", self.new_user);
std::io::stdin().read_line(&mut self.age);
let age2: String = self.age.trim().into();
}
fn acc_no(&mut self) {
println!(
"We will generate a new account number \
and routing number for you."
);
self.account_number = rand::thread_rng().gen_range(10000000..99999999);
println!("Your account number is {}", self.account_number);
self.routing_number = rand::thread_rng().gen_range(10000000..99999999);
println!("Your account routing number is {}", self.routing_number);
}
fn yes(self) {
NewUser::user_info(&mut self);
NewUser::acc_no(&mut self);
}
//function I want to use in main.
fn bank_new_user(self) {
self.account = String::new();
println!("Would you like to make a new account with us today?");
loop {
println!(
" yes: continue to application, no: continue browsing , \
or exit: to exit"
);
self.account.clear();
std::io::stdin()
.read_line(&mut self.account)
.expect("please type yes, no or exit.");
let account = self.account.trim();
match account {
"yes" => {
self.yes();
break;
}
"no" => {
println!("You do not need an account to continue browsing.");
println!("Have a wonderful day and thank you for considering Mars Banking!");
break;
}
"exit" => {
println!(
"Thank you for choosing Mars Banking for your banking needs!\
Have a wonderful day!"
);
break;
}
_ => {
println!("Error! Enter yes, no, or exit.")
}
}
}
}
}
pub mod new_user;
mod settings;
mod user_account;
pub use crate::settings::settings;
pub use crate::user_account::user_account;
use new_user::NewUser;
use new_user::UserInfo;
fn main() {
loop{
let mut select = String::new();
println!("Welcome to Mars Banking!");
println!("What would you like to do today?");
println!("Create a new account: 1\nLogin: 2\nSettings: 3\nExit: 4");
select.clear();
std::io::stdin().read_line(&mut select);
let select = select.trim();
match select {
//Here is where the error happens.
"1" => NewUser::bank_new_user(self),
"2" => user_account(),
"3" => settings(),
"4" => break,
_ => {}
}
}
}
The conventional pattern for this sort of constructor is a static method that doesn't take a self argument, like this:
impl NewUser {
fn bank_new_user() {
let mut new_user = NewUser { /* initialize the fields */ };
// Edit or use new_user as necessary
}
}
you can see an example of this here, in the methods defined for Point:
struct Point {
x: f64,
y: f64,
}
// Implementation block, all `Point` associated functions & methods go in here
impl Point {
// This is an "associated function" because this function is associated with
// a particular type, that is, Point.
//
// Associated functions don't need to be called with an instance.
// These functions are generally used like constructors.
fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
// Another associated function, taking two arguments:
fn new(x: f64, y: f64) -> Point {
Point { x: x, y: y }
}
}
notice how niether origin nor new take self as an argument.

Which signature is most effective when using multiple conditions or Results? How to bubble errors correctly?

Introduction
I'm learning rust and have been trying to find the right signature for using multiple Results in a single function and then returning either correct value, or exit the program with a message.
So far I have 2 different methods and I'm trying to combine them.
Context
This is what I'm trying to achieve:
fn blur(image: DynamicImage, amount: &str) -> DynamicImage {
let amount = parse_between_or_error_out("blur", amount, 0.0, 10.0);
image.brighten(amount)
}
This is what I have working now, but would like to refactor.
fn blur(image: DynamicImage, amount: &str) -> DynamicImage {
match parse::<f32>(amount) {
Ok(amount) => {
verify_that_value_is_between("blur", amount, 0.0, 10.0);
image.blur(amount)
}
_ => {
println!("Error");
process::exit(1)
}
}
}
Combining these methods
Now here's the two working methods that I'm trying to combine, to achieve this.
fn parse<T: FromStr>(value: &str) -> Result<T, <T as FromStr>::Err> {
value.parse::<T>()
}
fn verify_that_value_is_between<T: PartialOrd + std::fmt::Display>(
name: &str,
amount: T,
minimum: T,
maximum: T,
) {
if amount > maximum || amount < minimum {
println!(
"Error: Expected {} amount to be between {} and {}",
name, minimum, maximum
);
process::exit(1)
};
println!("- Using {} of {:.1}/{}", name, amount, maximum);
}
Here's what I tried
I have tried the following. I realise I'm likely doing a range of things wrong. This is because I'm still learning Rust, and I'd like any feedback that helps me learn how to improve.
fn parse_between_or_error_out<T: PartialOrd + FromStr + std::fmt::Display>(
name: &str,
amount: &str,
minimum: T,
maximum: T,
) -> Result<T, <T as FromStr>::Err> {
fn error_and_exit() {
println!(
"Error: Expected {} amount to be between {} and {}",
name, minimum, maximum
);
process::exit(1);
}
match amount.parse::<T>() {
Ok(amount) => {
if amount > maximum || amount < minimum {
error_and_exit();
};
println!("- Using {} of {:.1}/{}", name, amount, maximum);
amount
}
_ => {
error_and_exit();
}
}
}
Currently this looks quite messy, probably I'm using too many or the wrong types and the error needs to be in two places (hence the inlined function, which I know is not good practice).
Full reproducible example.
The question
How to best combine logic that is using a Result and another condition (or Result), exit with a message or give T as a result?
Comments on any of the mistakes are making are very welcome too.
You can use a crate such as anyhow to bubble your events up and handle them as needed.
Alternatively, you can write your own trait and implement it on Result.
trait PrintAndExit<T> {
fn or_print_and_exit(&self) -> T;
}
Then use it by calling the method on any type that implements it:
fn try_get_value() -> Result<bool, MyError> {
MyError { msg: "Something went wrong".to_string() }
}
let some_result: Result<bool, MyError> = try_get_value();
let value: bool = some_result.or_print_and_exit();
// Exits with message: "Error: Something went wrong"
Implementing this trait on Result could be done with:
struct MyError {
msg: String,
}
impl<T> PrintAndExit<T> for Result<T, MyError> {
fn or_print_and_exit(&self) -> T {
match self {
Ok(val) => val,
Err(e) => {
println!("Error: {}", e.msg);
std::process::exit(1);
},
}
}
}
Here are a few DRY tricks.
tl;dr:
Convert other Errors into your unified error type(s) with impl From<ExxError> for MyError;
In any function that may result in an Error, use ? as much as you can. Return Result<???, MyError> (*). ? will utilize the implicit conversion.
(*) Only if MyError is an appropriate type for the function. Always create or use the most appropriate error types. (Kinda obvious, but people often treat error types as a second-class code, pun intended)
Recommendations are in the comments.
use std::error::Error;
use std::str::FromStr;
// Debug and Display are required by "impl Error" below.
#[derive(Debug)]
enum ProcessingError {
NumberFormat{ message: String },
NumberRange{ message: String },
ProcessingError{ message: String },
}
// Display will be used when the error is printed.
// No need to litter the business logic with error
// formatting code.
impl Display for ProcessingError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ProcessingError::NumberFormat { message } =>
write!(f, "Number format error: {}", message),
ProcessingError::NumberRange { message } =>
write!(f, "Number range error: {}", message),
ProcessingError::ProcessingError { message } =>
write!(f, "Image processing error: {}", message),
}
}
}
impl Error for ProcessingError {}
// FromStr::Err will be implicitly converted into ProcessingError,
// when ProcessingError is needed. I guess this is what
// anyhow::Error does under the hood.
// Implement From<X> for ProcessingError for every X error type
// that your functions like process_image() may encounter.
impl From<FromStr::Err> for ProcessingError {
fn from(e: FromStr::Err) -> ProcessingError {
ProcessingError::NumberFormat { message: format!("{}", e) }
}
}
pub fn try_parse<T: FromStr>(value: &str) -> Result<T, ProcessingError> {
// Note ?. It will implicitly return
// Err(ProcessingError created from FromStr::Err)
Ok (
value.parse::<T>()?
)
}
// Now, we can have each function only report/handle errors that
// are relevant to it. ? magically eliminates meaningless code like
// match x { ..., Err(e) => Err(e) }.
pub fn parse_between<T>(value: &str, min_amount: T, max_amount: T)
-> Result<T, ProcessingError>
where
T: FromStr + PartialOrd + std::fmt::Display,
{
let amount = try_parse::<T>(value)?;
if amount > max_amount || amount < min_amount {
Err(ProcessingError::NumberRange {
message: format!(
"Expected value to be between {} and {} but received {}",
min_amount,
max_amount,
amount)
})
} else {
Ok(amount)
}
}
main.rs
use image::{DynamicImage};
use std::fmt::{Debug, Formatter, Display};
fn blur(image: DynamicImage, value: &str)
-> Result<DynamicImage, ProcessingError>
{
let min_amount = 0.0;
let max_amount = 10.0;
// Again, note ? in the end.
let amount = parse_between(value, min_amount, max_amount)?;
image.blur(amount)
}
// All processing extracted into a function, whose Error
// then can be handled by main().
fn process_image(image: DynamicImage, value: &str)
-> Result<DynamicImage, ProcessingError>
{
println!("applying blur {:.1}/{:.1}...", amount, max_amount);
image = blur(image, value);
// save image ...
image
}
fn main() {
let mut image = DynamicImage::new(...);
image = match process_image(image, "1") {
Ok(image) => image,
// No need to reuse print-and-exit functionality. I doubt
// you want to reuse it a lot.
// If you do, and then change your mind, you will have to
// root it out of all corners of your code. Better return a
// Result and let the caller decide what to do with errors.
// Here's a single point to process errors and exit() or do
// something else.
Err(e) => {
println!("Error processing image: {:?}", e);
std::process::exit(1);
}
}
}
Sharing my results
I'll share my results/answer as well for other people who are new to Rust. This answer is based on that of #Acidic9's answer.
The types seem to be fine
anyhow looks to be the de facto standard in Rust.
I should have used a trait and implement that trait for the Error type.
I believe the below example is close to what it might look like in the wild.
// main.rs
use image::{DynamicImage};
use app::{parse_between, PrintAndExit};
fn main() {
// mut image = ...
image = blur(image, "1")
// save image
}
fn blur(image: DynamicImage, value: &str) -> DynamicImage {
let min_amount = 0.0;
let max_amount = 10.0;
match parse_between(value, min_amount, max_amount).context("Input error") {
Ok(amount) => {
println!("applying blur {:.1}/{:.1}...", amount, max_amount);
image.blur(amount)
}
Err(error) => error.print_and_exit(),
}
}
And the implementation inside the apps library, using anyhow.
// lib.rs
use anyhow::{anyhow, Error, Result};
use std::str::FromStr;
pub trait Exit {
fn print_and_exit(self) -> !;
}
impl Exit for Error {
fn print_and_exit(self) -> ! {
eprintln!("{:#}", self);
std::process::exit(1);
}
}
pub fn try_parse<T: FromStr>(value: &str) -> Result<T, Error> {
match value.parse::<T>() {
Ok(value) => Ok(value),
Err(_) => Err(anyhow!("\"{}\" is not a valid value.", value)),
}
}
pub fn parse_between<T>(value: &str, min_amount: T, max_amount: T) -> Result<T, Error>
where
T: FromStr + PartialOrd + std::fmt::Display,
{
match try_parse::<T>(value) {
Ok(amount) => {
if amount > max_amount || amount < min_amount {
return Err(anyhow!(
"Expected value to be between {} and {} but received {}",
min_amount,
max_amount,
amount
));
};
Ok(amount)
}
Err(error) => Err(error),
}
}
Hopefully seeing this full implementation will help someone out there.
Source code.

Creating an iterator that either steps upwards or downwards

I'd ideally like to have something like the following:
iter = if go_up {
(min .. limit)
} else {
(limit .. max).rev()
};
to create an iterator that either counts up or down to some limit, depending on the situation. However, because Range and Rev are different types, I can't do this. I can use the step_by feature, but because my limits are an unsigned data-type, I then also have to cast everything. The best I have so far is:
#![feature(step_by)]
iter = if go_up {
(min as i64 .. limit as i64).step_by(1)
} else {
(limit as i64 .. max as i64).step_by(-1)
};
but this requires both unstable features, and shoehorning my types. It seems like there should be a neater way to do this; does anyone know one?
The direct solution is to simply create an iterator that can either count upwards or downwards. Use an enum to choose between the types:
use std::ops::Range;
use std::iter::Rev;
enum Foo {
Upwards(Range<u8>),
Downwards(Rev<Range<u8>>),
}
impl Foo {
fn new(min: u8, limit: u8, max: u8, go_up: bool) -> Foo {
if go_up {
Foo::Upwards(min..limit)
} else {
Foo::Downwards((limit..max).rev())
}
}
}
impl Iterator for Foo {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
match *self {
Foo::Upwards(ref mut i) => i.next(),
Foo::Downwards(ref mut i) => i.next(),
}
}
}
fn main() {
for i in Foo::new(1, 5, 10, true) {
println!("{}", i);
}
for i in Foo::new(1, 5, 10, false) {
println!("{}", i);
}
}
Another pragmatic solution that introduces a little bit of indirection is to Box the iterators:
fn thing(min: u8, limit: u8, max: u8, go_up: bool) -> Box<Iterator<Item = u8>> {
if go_up {
Box::new(min..limit)
} else {
Box::new((limit..max).rev())
}
}
fn main() {
for i in thing(1, 5, 10, true) {
println!("{}", i);
}
for i in thing(1, 5, 10, false) {
println!("{}", i);
}
}
Personally, your solution
iter = if go_up {
(min as i64 .. limit as i64).step_by(1)
} else {
(limit as i64 .. max as i64).step_by(-1)
};
is a better option than Shepmaster's first example, since it's more complete (eg. there's a size_hint), it's more likely to be correct by virtue of being a standard tool and it's faster to write.
It's true that this is unstable, but there's nothing stopping you from just copying the source in the meantime. That gives you a nice upgrade path for when this eventually gets stabilized.
The enum wrapper technique is great in more complex cases, though, but in this case I'd be tempted to KISS.

Resources