Is there a better way to infinitely error check other than recursion? - recursion

I'm trying to make a Tic-Tac-Toe game with a custom board size. I want this to be very hard to break, so I use recursion to get the board measurements if the input is invalid or an error occurs. However, this doesn't seem very clean to me, and I was wondering if there's a better/more rusty way of achieving the same thing.
Code in main function
let board_size_str = get_board_size_string();
let (x_pos, width, height) = get_board_measurement(&board_size_str);
Functions
fn get_board_size_string() -> String {
println!("Select the size of the board in the following format: 5x5 or 7x7");
println!("The size can be from 3x3 to 30x30");
print!("Size: ");
std::io::stdout().flush().expect("Failed to flush stdout!");
let mut board_size_str = String::new();
std::io::stdin().read_line(&mut board_size_str).expect("Failed to read board size!");
println!();
board_size_str
}
fn get_board_measurement(board_size_str: &str) -> (usize, i64, i64) {
let x_pos = get_x_pos(board_size_str);
let width = get_board_width(board_size_str, x_pos);
let height = get_board_height(board_size_str, x_pos);
(x_pos, width, height)
}
fn get_x_pos(board_size_str: &str) -> usize {
let x_pos_option = board_size_str.chars().position(|c| c == 'x');
match x_pos_option {
Some(x_pos) => x_pos,
None => {
println!("Board size must contain an x!");
let board_size_str = get_board_size_string();
get_x_pos(&board_size_str)
}
}
}
fn get_board_width(board_size_str: &str, x_pos: usize) -> i64 {
let width_result = board_size_str[..x_pos].parse::<i64>();
match width_result {
Ok(width) => width,
Err(_) => {
println!("Invalid board width!");
let board_size_str = get_board_size_string();
get_board_width(&board_size_str, get_x_pos(&board_size_str))
}
}
}
fn get_board_height(board_size_str: &str, x_pos: usize) -> i64 {
let height_result = board_size_str[x_pos + 1..].trim().parse::<i64>();
match height_result {
Ok(height) => height,
Err(_) => {
println!("Invalid board height!");
let board_size_str = get_board_size_string();
get_board_height(&board_size_str, get_x_pos(&board_size_str))
}
}
}

Just use an iterative loop?
fn get_x_pos(board_size_str: &str) -> usize {
loop {
let board_size_str = get_board_size_string();
let x_pos_option = board_size_str.chars().position(|c| c == 'x');
if let Some(x_pos) = x_pos_option {
break x_pos
}
}
}
Though the structure is strange because a correct board size is a correct pattern ( 'x' ) so it's not like splitting that into three unrelated routines makes any sense, even if two of them do delegate the localisation of the x separator.
With your method you can input something like 52xkf, get an error, input 24x36, and I think you'll get a 52x36 board rather than the 24x36 you might expect, which is just odd. Would be a lot easier to just do the entire thing in a single pseudo-step:
fn parse_board_size() -> (usize, usize) {
loop {
let s = get_board_size_string();
let Some((w_s, h_s)) = s.split_once('x') else {
// complain about a missing `x` here
continue;
};
match (w_s.parse(), h_s.parse()) {
(Ok(w), Ok(s)) => {
// can add more validation here,
// or as pattern guards
return (w, s);
}
(Ok(_), Err(h_error)) => {
// h was incorrect
}
(Err(w_error), Ok(_)) => {
// w was incorrect
}
(Err(w_error), Err(h_error)) => {
// both were incorrect
}
}
}
}
Alternatively for the parsing if you don't care about custom-reporting each error case individually you can lean on Option e.g.
fn parse_board_size() -> (usize, usize) {
loop {
let s = get_board_size_string();
let Some((w_s, h_s)) = s.split_once('x') else {
// complain about a missing `x` here
continue;
};
if let Some(r) = w_s.parse().ok().zip(h_s.parse().ok()) {
break r;
}
// report generic parsing error
}
}

Related

Searching a Vec for a match

My first Rust program compiles and runs:
use structopt::StructOpt;
use pcap::{Device,Capture};
use std::process::exit;
#[derive(StructOpt)]
struct Cli {
/// the capture device
device: String,
}
fn main() {
let devices = Device::list();
let args = Cli::from_args();
let mut optdev :Option<Device> = None;
for d in devices.unwrap() {
//println!("device: {:?}", d);
if d.name == args.device {
optdev = Some(d);
}
}
let dev = match optdev {
None => {
println!("Device {} not found.", args.device);
exit(1);
},
Some(dev) => dev,
};
let mut cap = Capture::from_device(dev).unwrap()
.promisc(true)
.snaplen(100)
.open().unwrap();
while let Ok(packet) = cap.next() {
println!("received packet! {:?}", packet);
}
}
I have some complex code which iterates through the Vec of devices, testing each one's .name property against args.device.
I'm guessing that there is a method of 'looking-up' an entry in a Vec, such that I can replace all the optdev lines with something like:
let dev = match devices.unwrap().look_up(.name == args.device) {
None => {
println!("Device {} not found.", args.device);
exit(1);
},
Some(dev) => dev,
};
What is the syntax for such a look_up()?
Or is there a more idiomatic way of doing this?
What is the syntax for such a look_up()?
Iterator::find. Since the operation is not specific to vectors (or slices), it doesn't live there, and is applicable to any iterator instead.
It'd look something like this:
let dev = match devices.unwrap().into_iter().find(|d| d.name == args.device) {
None => {
println!("Device {} not found.", args.device);
exit(1);
},
Some(dev) => dev,
};
or
let dev = if let Some(dev) = devices.unwrap().into_iter().find(|d| d.name == args.device) {
dev
} else {
println!("Device {} not found.", args.device);
exit(1);
};
(side-note: you may also want to use eprintln for, well, error reporting).
Though a somewhat cleaner error handling could be along the lines of (note: not tested so there might be semantic or syntactic mistakes):
use std::fmt;
use std:errors::Error;
#[derive(Debug)]
struct NoDevice(String);
impl fmt::Display for NoDevice {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Device {} not found", self.0)
}
}
impl Error for NoDevice {}
fn main() -> Result<(), Box<dyn Error>> {
let devices = Device::list()?;
let args = Cli::from_args();
let dev = devices.into_iter()
.find(|d| d.name == args.device)
.ok_or_else(|| NoDevice(args.device))?
let mut cap = Capture::from_device(dev)?
.promisc(true)
.snaplen(100)
.open()?;
while let Ok(packet) = cap.next() {
println!("received packet! {:?}", packet);
}
}

How do I build an iterator for walking a file tree recursively?

I want to lazily consume the nodes of a file tree one by one while sorting the siblings on each level.
In Python, I'd use a synchronous generator:
def traverse_dst(src_dir, dst_root, dst_step):
"""
Recursively traverses the source directory and yields a sequence of (src, dst) pairs;
"""
dirs, files = list_dir_groom(src_dir) # Getting immediate offspring.
for d in dirs:
step = list(dst_step)
step.append(d.name)
yield from traverse_dst(d, dst_root, step)
for f in files:
dst_path = dst_root.joinpath(step)
yield f, dst_path
In Elixir, a (lazy) stream:
def traverse_flat_dst(src_dir, dst_root, dst_step \\ []) do
{dirs, files} = list_dir_groom(src_dir) # Getting immediate offspring.
traverse = fn d ->
step = dst_step ++ [Path.basename(d)]
traverse_flat_dst(d, dst_root, step)
end
handle = fn f ->
dst_path =
Path.join(
dst_root,
dst_step
)
{f, dst_path}
end
Stream.flat_map(dirs, traverse)
|> Stream.concat(Stream.map(files, handle))
end
One can see some language features addressing recursion: yield from in Python, flat_map in Elixir; the latter looks like a classic functional approach.
It looks like whatever is lazy in Rust, it's always an iterator. How am I supposed to do more or less the same in Rust?
I'd like to preserve the structure of my recursive function with dirs and files as vectors of paths (they are optionally sorted and filtered).
Getting dirs and files is already implemented to my liking:
fn folders(dir: &Path, folder: bool) -> Result<Vec<PathBuf>, io::Error> {
Ok(fs::read_dir(dir)?
.into_iter()
.filter(|r| r.is_ok())
.map(|r| r.unwrap().path())
.filter(|r| if folder { r.is_dir() } else { !r.is_dir() })
.collect())
}
fn list_dir_groom(dir: &Path) -> (Vec<PathBuf>, Vec<PathBuf>) {
let mut dirs = folders(dir, true).unwrap();
let mut files = folders(dir, false).unwrap();
if flag("x") {
dirs.sort_unstable();
files.sort_unstable();
} else {
sort_path_slice(&mut dirs);
sort_path_slice(&mut files);
}
if flag("r") {
dirs.reverse();
files.reverse();
}
(dirs, files)
}
Vec<PathBuf can be iterated as is, and there is standard flat_map method. It should be possible to implement Elixir style, I just can't figure it out yet.
This is what I already have. Really working (traverse_flat_dst(&SRC, [].to_vec());), I mean:
fn traverse_flat_dst(src_dir: &PathBuf, dst_step: Vec<PathBuf>) {
let (dirs, files) = list_dir_groom(src_dir);
for d in dirs.iter() {
let mut step = dst_step.clone();
step.push(PathBuf::from(d.file_name().unwrap()));
println!("d: {:?}; step: {:?}", d, step);
traverse_flat_dst(d, step);
}
for f in files.iter() {
println!("f: {:?}", f);
}
}
What I want (not yet working!):
fn traverse_flat_dst_iter(src_dir: &PathBuf, dst_step: Vec<PathBuf>) {
let (dirs, files) = list_dir_groom(src_dir);
let traverse = |d| {
let mut step = dst_step.clone();
step.push(PathBuf::from(d.file_name().unwrap()));
traverse_flat_dst_iter(d, step);
};
// This is something that I just wish to be true!
flat_map(dirs, traverse) + map(files)
}
I want this function to deliver one long flat iterator of files, in the spirit of the Elixir solution. I just can't yet cope with the necessary return types and other syntax. I really hope to be clear enough this time.
What I managed to compile and run (meaningless, but the signature is what I actually want):
fn traverse_flat_dst_iter(
src_dir: &PathBuf,
dst_step: Vec<PathBuf>,
) -> impl Iterator<Item = (PathBuf, PathBuf)> {
let (dirs, files) = list_dir_groom(src_dir);
let _traverse = |d: &PathBuf| {
let mut step = dst_step.clone();
step.push(PathBuf::from(d.file_name().unwrap()));
traverse_flat_dst_iter(d, step)
};
files.into_iter().map(|f| (f, PathBuf::new()))
}
What I'm still lacking:
fn traverse_flat_dst_iter(
src_dir: &PathBuf,
dst_step: Vec<PathBuf>,
) -> impl Iterator<Item = (PathBuf, PathBuf)> {
let (dirs, files) = list_dir_groom(src_dir);
let traverse = |d: &PathBuf| {
let mut step = dst_step.clone();
step.push(PathBuf::from(d.file_name().unwrap()));
traverse_flat_dst_iter(d, step)
};
// Here is a combination amounting to an iterator,
// which delivers a (PathBuf, PathBuf) tuple on each step.
// Flat mapping with traverse, of course (see Elixir solution).
// Iterator must be as long as the number of files in the tree.
// The lines below look very close, but every possible type is mismatched :(
dirs.into_iter().flat_map(traverse)
.chain(files.into_iter().map(|f| (f, PathBuf::new())))
}
There are two approaches:
The first one is to use an existing crate, like walkdir. The benefit is it's being well tested and offers many options.
The second one is to write your own implementation of Iterator. Here's an example, and maybe the basis for your own:
struct FileIterator {
dirs: Vec<PathBuf>, // the dirs waiting to be read
files: Option<ReadDir>, // non recursive iterator over the currently read dir
}
impl From<&str> for FileIterator {
fn from(path: &str) -> Self {
FileIterator {
dirs: vec![PathBuf::from(path)],
files: None,
}
}
}
impl Iterator for FileIterator {
type Item = PathBuf;
fn next(&mut self) -> Option<PathBuf> {
loop {
while let Some(read_dir) = &mut self.files {
match read_dir.next() {
Some(Ok(entry)) => {
let path = entry.path();
if let Ok(md) = entry.metadata() {
if md.is_dir() {
self.dirs.push(path.clone());
continue;
}
}
return Some(path);
}
None => { // we consumed this directory
self.files = None;
break;
}
_ => { }
}
}
while let Some(dir) = self.dirs.pop() {
let read_dir = fs::read_dir(&dir);
if let Ok(files) = read_dir {
self.files = Some(files);
return Some(dir);
}
}
break; // no more files, no more dirs
}
return None;
}
}
playground
The advantage of writing your own iterator is that you'll tune it for your precise needs (sorting, filtering, error handling, etc.). But you'll have to deal with your own bugs.
This is the exact solution I sought. It's none of my achievement; see here. Comments are welcome.
fn traverse_flat_dst_iter(
src_dir: &PathBuf,
dst_step: Vec<PathBuf>,
) -> impl Iterator<Item = (PathBuf, PathBuf)> {
let (dirs, files) = list_dir_groom(src_dir);
let traverse = move |d: PathBuf| -> Box<dyn Iterator<Item = (PathBuf, PathBuf)>> {
let mut step = dst_step.clone();
step.push(PathBuf::from(d.file_name().unwrap()));
Box::new(traverse_flat_dst_iter(&d, step))
};
dirs.into_iter()
.flat_map(traverse)
.chain(files.into_iter().map(|f| (f, PathBuf::new())))
}
Another, more sophisticated take. One has to box things, clone parameters to be shared between lambdas, etc., to satisfy the compiler. Yet it works. Hopefully, on can get the hang of the thing.
fn traverse_dir(
src_dir: &PathBuf,
dst_step: Vec<PathBuf>,
) -> Box<dyn Iterator<Item = (PathBuf, Vec<PathBuf>)>> {
let (dirs, files) = groom(src_dir);
let destination_step = dst_step.clone(); // A clone for handle.
let traverse = move |d: PathBuf| {
let mut step = dst_step.clone();
step.push(PathBuf::from(d.file_name().unwrap()));
traverse_dir(&d, step)
};
let handle = move |f: PathBuf| (f, destination_step.clone());
if flag("r") {
// Chaining backwards.
Box::new(
files
.into_iter()
.map(handle)
.chain(dirs.into_iter().flat_map(traverse)),
)
} else {
Box::new(
dirs.into_iter()
.flat_map(traverse)
.chain(files.into_iter().map(handle)),
)
}
}

How to replace combinators with Future?

I have a function which returns Future. It accepts another function which accepts one argument and returns Future. Second function can be implemented as combinators chain passed into first function. It looks like this:
use bb8::{Pool, RunError};
use bb8_postgres::PostgresConnectionManager;
use tokio_postgres::{error::Error, Client, NoTls};
#[derive(Clone)]
pub struct DataManager(Pool<PostgresConnectionManager<NoTls>>);
impl DataManager {
pub fn new(pool: Pool<PostgresConnectionManager<NoTls>>) -> Self {
Self(pool)
}
pub fn create_user(
&self,
reg_req: UserRequest,
) -> impl Future<Item = User, Error = RunError<Error>> {
let sql = "long and awesome sql";
let query = move |mut conn: Client| { // function which accepts one argument and returns Future
conn.prepare(sql).then(move |r| match r {
Ok(select) => {
let f = conn
.query(&select, &[&reg_req.email, &reg_req.password])
.collect()
.map(|mut rows| {
let row = rows.remove(0);
row.into()
})
.then(move |r| match r {
Ok(v) => Ok((v, conn)),
Err(e) => Err((e, conn)),
});
Either::A(f)
}
Err(e) => Either::B(future::err((e, conn))),
})
};
self.0.run(query) // function which returns Future and accepts another function
}
}
But I want to write code of create_user as a struct implementing Future.
struct UserCreator(Pool<PostgresConnectionManager<NoTls>>, UserRequest);
impl UserCreator {
fn new(pool: Pool<PostgresConnectionManager<NoTls>>, reg_req: UserRequest) -> Self {
Self(pool, reg_req)
}
}
How to implement Future for this struct that works as first function? Please help me with an example.
Now I tried to make it like this, but nothing is computed and execution always blocks.
impl Future for UserCreator {
type Item = User;
type Error = RunError<Error>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
// Code which which works like `DataManager.create_user`
let sql = "long and awesome sql";
let reg_req = &self.1;
let query = move |mut conn: Client| {
conn.prepare(sql).then(move |r| match r {
Ok(select) => {
let f = conn
.query(&select, &[&reg_req.email, &reg_req.password])
.collect()
.map(|mut rows| {
let row = rows.remove(0);
row.into()
})
.then(move |r| match r {
Ok(v) => Ok((v, conn)),
Err(e) => Err((e, conn)),
});
Either::A(f)
}
Err(e) => Either::B(future::err((e, conn))),
})
};
self.0.run(query).poll()
}
}

What is an efficient way to reset all values of a Vec<T> without resizing it?

I can use resize, but it seems like overkill because I do not need to resize the vector, just modify its values. Using a new variable is not an option, since this vector is actually a field in a struct.
I guess that resize is efficient, and probably the answer to my question, but its name does not carry the meaning of resetting the values without modifying the size.
In C, I would use memset (in opposition to realloc).
Illustration of my question:
let my_vec_size = 42;
let mut my_vec = Vec::new(); // 'my_vec' will always have a size of 42
my_vec.resize(my_vec_size, false); // Set the size to 42, and all values to false
// [ ... ] piece of code where the values in 'my_vec' will be modified, checked, etc ...
// now I need to reuse my_vec.
// Possibility A -> use resize again
my_vec.resize(my_vec_size, false);
// Possibility B -> iterate on the vector to modify its values (long and laborious)
for item in my_vec.iter_mut() {
*item = false;
}
// Possibility C ?
The most efficient way in general is to reset the values themselves (aka B):
for item in &mut my_vec { *item = false; }
For booleans it is not immediately obvious, however for a String it is important to preserve the allocated buffer of each element:
for item in &mut my_vec { item.clear(); }
If discarding and recreating the elements of the Vec is cheap, such as the case of the boolean or if the elements will be overwritten anyway, then a combination of clear and resize is easier:
my_vec.clear();
my_vec.resize(my_vec_size, false);
resize by itself will not work to "reset" values:
const LEN: usize = 3;
fn main() {
let mut values = vec![false; LEN];
values[0] = true;
values.resize(LEN, false);
println!("{:?}", values); // [true, false, false]
}
Just use a for loop:
for v in &mut values {
*v = false;
}
println!("{:?}", values); // [false, false, false]
If that sight offends you, write an extension trait:
trait ResetExt<T: Copy> {
fn reset(&mut self, val: T);
}
impl<T: Copy> ResetExt<T> for [T] {
fn reset(&mut self, value: T) {
for v in self {
*v = value;
}
}
}
values.reset(false);
println!("{:?}", values); // [false, false, false]
The trait idea can be extended so that each value knows how to reset itself, if that makes sense for your situation:
trait ResetExt {
fn reset(&mut self);
}
impl<T: ResetExt> ResetExt for [T] {
fn reset(&mut self) {
for v in self {
v.reset();
}
}
}
impl ResetExt for bool {
fn reset(&mut self) {
*self = false;
}
}
impl ResetExt for String {
fn reset(&mut self) {
self.clear();
}
}
values.reset();
println!("{:?}", values); // [false, false, false]
In C, I would use memset
std::ptr::write_bytes uses memset internally, so you can (almost) precisely translate this code. An example from the Rust documentation:
let mut vec = vec![0u32; 4];
unsafe {
let vec_ptr = vec.as_mut_ptr();
ptr::write_bytes(vec_ptr, 0xfe, 2);
}
assert_eq!(vec, [0xfefefefe, 0xfefefefe, 0, 0]);

Accessing the last element of a Vec or a slice

I have some code that looks like this:
trait Stack {
fn top(&mut self) -> Option<f64>;
}
impl Stack for Vec<f64> {
fn top(&mut self) -> Option<f64> {
match self.pop() {
None => None,
Some(v) => {
self.push(v);
Some(v)
}
}
}
}
fn main() {
let mut stack: Vec<f64> = Vec::new();
stack.push(5.3);
stack.push(2.3);
stack.push(1.3);
match stack.top() {
Some(v) => println!("Top of the stack: {}", v),
None => println!("The stack is empty"),
}
}
Right now, the top() method is modifying self, but I think that this should not be necessary. The obvious way to do it didn't really work:
fn top(&mut self) -> Option<f64> {
match self.len() {
0 => None,
n => self[n - 1],
}
}
I've toyed around a bit with converting usize to i32 and back, but none of what I'm writing looks as short and readable as I think it should.
You can use slice::last:
fn top(&mut self) -> Option<f64> {
self.last().copied()
}
Option::copied (and Option::cloned) can be used to convert from an Option<&f64> to an Option<f64>, matching the desired function signature.
You can also remove the mut from both the implementation and the trait definition.
And just after posting the question, the answer appears to be obvious:
fn top (&mut self) -> Option<&f64> {
match self.len() {
0 => None,
n => Some(&self[n-1])
}
}
I.e. the usize was never the problem - the return type of top() was.

Resources