What if we wanted to read our shapes from a file?
FromStr
allows for a &str
to become a Type
through the method parse
You may remember from earlier, this code
// -------v
let foo: usize = "5".parse(); // <-- parse infers the usize parse
// due to type
Before we implement a FromStr for both Circle and Rect
Whenever i deal with errors in small applications, at this point I just default
to using the anyhow
crate. It makes working with errors very simple.
execute cargo add anyhow
➜ rust-typescript git:(master) ✗ cargo add anyhow
Updating crates.io index
Adding anyhow (workspace) to dependencies.
Features as of v1.0.68:
+ std
- backtrace
Ok, lets use Anyhow and FromStr
We will implement a Circle
and Rectangle
parse
Complete Code
src/shapes/rect.rs
impl FromStr for Rectangle {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split(" ").collect();
if parts.len() != 4 {
return Err(anyhow::anyhow!("Invalid number of parts"));
}
return Ok(Rectangle {
x: parts[0].parse()?,
y: parts[1].parse()?,
width: parts[2].parse()?,
height: parts[3].parse()?,
});
}
}
src/shapes/circle.rs
impl FromStr for Circle {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<&str> = s.split(" ").collect();
if parts.len() != 3 {
return Err(anyhow::anyhow!("Invalid number of parts"));
}
return Ok(Circle {
x: parts[0].parse()?,
y: parts[1].parse()?,
radius: parts[2].parse()?,
});
}
}
Lets create a file!
We will use this file, parse out the contents, build our circle and rects, and test to see if any circles intesect our rect (though... its not a good formula).
rect 0 0 10 20
circle 12 20 1
circle 0 20 4
rect 10 10 10 10
Requires
- reading a file (we have done)
- create an enum to store either a
Circle
orRect
in - read each line and perhaps use
split_once
(recommend using pattern matching) - find any adjancent collision
You have done most of this so far throughout the day, lets see if you can do this by yourself.
Complete Code
src/main.rs
mod shapes;
use std::{str::FromStr, fmt::Display};
use anyhow::Result;
use shapes::{rect::Rectangle, circle::Circle, collisions::{Points, Contains, Collidable}};
enum Shape {
Rect(Rectangle),
Circ(Circle),
}
impl FromStr for Shape {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (shape, data) = s.split_once(" ")
.ok_or(anyhow::anyhow!("Invalid shape"))?;
match shape {
"circle" => return Ok(Shape::Circ(data.parse()?)),
"rect" => return Ok(Shape::Rect(data.parse()?)),
_ => return Err(anyhow::anyhow!("Invalid shape"))
}
}
}
impl Points for Shape {
fn points(&self) -> shapes::collisions::PointIter {
match self {
Shape::Rect(rect) => return rect.points(),
Shape::Circ(circ) => return circ.points(),
}
}
}
impl Contains for Shape {
fn contains_point(&self, point: (f64, f64)) -> bool {
match self {
Shape::Rect(rect) => return rect.contains_point(point),
Shape::Circ(circ) => return circ.contains_point(point),
}
}
}
impl Display for Shape {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Shape::Rect(rect) => return write!(f, "{}", rect),
Shape::Circ(circ) => return write!(f, "{}", circ),
}
}
}
fn main() -> Result<()> {
let file = std::fs::read_to_string("shapes")?;
let shapes = file
.lines()
.filter_map(|line| line.parse().ok())
.collect::<Vec<Shape>>();
let collisions: Vec<(&Shape, &Shape)> = shapes
.iter()
.skip(1)
.zip(shapes.iter().take(shapes.len() - 1))
.filter(|(a, b)| a.collide(b))
.collect();
for (a, b) in collisions {
println!("Collision: {} {}", a, b);
}
return Ok(());
}