第三天上午的练习
Drawing A Simple GUI
(返回练习)
pub trait Widget { /// Natural width of `self`. fn width(&self) -> usize; /// Draw the widget into a buffer. fn draw_into(&self, buffer: &mut dyn std::fmt::Write); /// Draw the widget on standard output. fn draw(&self) { let mut buffer = String::new(); self.draw_into(&mut buffer); println!("{buffer}"); } } pub struct Label { label: String, } impl Label { fn new(label: &str) -> Label { Label { label: label.to_owned(), } } } pub struct Button { label: Label, } impl Button { fn new(label: &str) -> Button { Button { label: Label::new(label), } } } pub struct Window { title: String, widgets: Vec<Box<dyn Widget>>, } impl Window { fn new(title: &str) -> Window { Window { title: title.to_owned(), widgets: Vec::new(), } } fn add_widget(&mut self, widget: Box<dyn Widget>) { self.widgets.push(widget); } fn inner_width(&self) -> usize { std::cmp::max( self.title.chars().count(), self.widgets.iter().map(|w| w.width()).max().unwrap_or(0), ) } } impl Widget for Window { fn width(&self) -> usize { // Add 4 paddings for borders self.inner_width() + 4 } fn draw_into(&self, buffer: &mut dyn std::fmt::Write) { let mut inner = String::new(); for widget in &self.widgets { widget.draw_into(&mut inner); } let inner_width = self.inner_width(); // TODO: after learning about error handling, you can change // draw_into to return Result<(), std::fmt::Error>. Then use // the ?-operator here instead of .unwrap(). writeln!(buffer, "+-{:-<inner_width$}-+", "").unwrap(); writeln!(buffer, "| {:^inner_width$} |", &self.title).unwrap(); writeln!(buffer, "+={:=<inner_width$}=+", "").unwrap(); for line in inner.lines() { writeln!(buffer, "| {:inner_width$} |", line).unwrap(); } writeln!(buffer, "+-{:-<inner_width$}-+", "").unwrap(); } } impl Widget for Button { fn width(&self) -> usize { self.label.width() + 8 // add a bit of padding } fn draw_into(&self, buffer: &mut dyn std::fmt::Write) { let width = self.width(); let mut label = String::new(); self.label.draw_into(&mut label); writeln!(buffer, "+{:-<width$}+", "").unwrap(); for line in label.lines() { writeln!(buffer, "|{:^width$}|", &line).unwrap(); } writeln!(buffer, "+{:-<width$}+", "").unwrap(); } } impl Widget for Label { fn width(&self) -> usize { self.label .lines() .map(|line| line.chars().count()) .max() .unwrap_or(0) } fn draw_into(&self, buffer: &mut dyn std::fmt::Write) { writeln!(buffer, "{}", &self.label).unwrap(); } } fn main() { let mut window = Window::new("Rust GUI Demo 1.23"); window.add_widget(Box::new(Label::new("This is a small text GUI demo."))); window.add_widget(Box::new(Button::new( "Click me!" ))); window.draw(); }
点和多边形
(返回练习)
#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Point { x: i32, y: i32, } impl Point { pub fn new(x: i32, y: i32) -> Point { Point { x, y } } pub fn magnitude(self) -> f64 { f64::from(self.x.pow(2) + self.y.pow(2)).sqrt() } pub fn dist(self, other: Point) -> f64 { (self - other).magnitude() } } impl std::ops::Add for Point { type Output = Self; fn add(self, other: Self) -> Self::Output { Self { x: self.x + other.x, y: self.y + other.y, } } } impl std::ops::Sub for Point { type Output = Self; fn sub(self, other: Self) -> Self::Output { Self { x: self.x - other.x, y: self.y - other.y, } } } pub struct Polygon { points: Vec<Point>, } impl Polygon { pub fn new() -> Polygon { Polygon { points: Vec::new() } } pub fn add_point(&mut self, point: Point) { self.points.push(point); } pub fn left_most_point(&self) -> Option<Point> { self.points.iter().min_by_key(|p| p.x).copied() } pub fn iter(&self) -> impl Iterator<Item = &Point> { self.points.iter() } pub fn length(&self) -> f64 { if self.points.is_empty() { return 0.0; } let mut result = 0.0; let mut last_point = self.points[0]; for point in &self.points[1..] { result += last_point.dist(*point); last_point = *point; } result += last_point.dist(self.points[0]); result // Alternatively, Iterator::zip() lets us iterate over the points as pairs // but we need to pair each point with the next one, and the last point // with the first point. The zip() iterator is finished as soon as one of // the source iterators is finished, a neat trick is to combine Iterator::cycle // with Iterator::skip to create the second iterator for the zip and using map // and sum to calculate the total length. } } pub struct Circle { center: Point, radius: i32, } impl Circle { pub fn new(center: Point, radius: i32) -> Circle { Circle { center, radius } } pub fn circumference(&self) -> f64 { 2.0 * std::f64::consts::PI * f64::from(self.radius) } pub fn dist(&self, other: &Self) -> f64 { self.center.dist(other.center) } } pub enum Shape { Polygon(Polygon), Circle(Circle), } impl From<Polygon> for Shape { fn from(poly: Polygon) -> Self { Shape::Polygon(poly) } } impl From<Circle> for Shape { fn from(circle: Circle) -> Self { Shape::Circle(circle) } } impl Shape { pub fn perimeter(&self) -> f64 { match self { Shape::Polygon(poly) => poly.length(), Shape::Circle(circle) => circle.circumference(), } } } #[cfg(test)] mod tests { use super::*; fn round_two_digits(x: f64) -> f64 { (x * 100.0).round() / 100.0 } #[test] fn test_point_magnitude() { let p1 = Point::new(12, 13); assert_eq!(round_two_digits(p1.magnitude()), 17.69); } #[test] fn test_point_dist() { let p1 = Point::new(10, 10); let p2 = Point::new(14, 13); assert_eq!(round_two_digits(p1.dist(p2)), 5.00); } #[test] fn test_point_add() { let p1 = Point::new(16, 16); let p2 = p1 + Point::new(-4, 3); assert_eq!(p2, Point::new(12, 19)); } #[test] fn test_polygon_left_most_point() { let p1 = Point::new(12, 13); let p2 = Point::new(16, 16); let mut poly = Polygon::new(); poly.add_point(p1); poly.add_point(p2); assert_eq!(poly.left_most_point(), Some(p1)); } #[test] fn test_polygon_iter() { let p1 = Point::new(12, 13); let p2 = Point::new(16, 16); let mut poly = Polygon::new(); poly.add_point(p1); poly.add_point(p2); let points = poly.iter().cloned().collect::<Vec<_>>(); assert_eq!(points, vec![Point::new(12, 13), Point::new(16, 16)]); } #[test] fn test_shape_perimeters() { let mut poly = Polygon::new(); poly.add_point(Point::new(12, 13)); poly.add_point(Point::new(17, 11)); poly.add_point(Point::new(16, 16)); let shapes = vec![ Shape::from(poly), Shape::from(Circle::new(Point::new(10, 20), 5)), ]; let perimeters = shapes .iter() .map(Shape::perimeter) .map(round_two_digits) .collect::<Vec<_>>(); assert_eq!(perimeters, vec![15.48, 31.42]); } } fn main() {}