From 2927097382be5aad0b3737c2a70d544a8a030f2e Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Sun, 6 Jun 2021 20:10:12 -0700 Subject: [PATCH] complex: Update for 2nd edition. --- complex/Cargo.toml | 1 + complex/src/lib.rs | 364 +++++++++++++++++++++++++++------------------ 2 files changed, 221 insertions(+), 144 deletions(-) diff --git a/complex/Cargo.toml b/complex/Cargo.toml index f95d50b..5f63568 100644 --- a/complex/Cargo.toml +++ b/complex/Cargo.toml @@ -2,5 +2,6 @@ name = "complex" version = "0.1.0" authors = ["You "] +edition = "2018" [dependencies] diff --git a/complex/src/lib.rs b/complex/src/lib.rs index f04c050..1bc039d 100644 --- a/complex/src/lib.rs +++ b/complex/src/lib.rs @@ -1,192 +1,268 @@ -#[derive(Clone, Copy, Debug)] -struct Complex { - /// Real portion of the complex number - re: T, +//! Complex number examples. +//! +//! The chapter presents several different variations on how one might define +//! arithmetic on a generic `Complex` type, so what we have here are a bunch of +//! isolated modules, each of which defines its own `Complex` type in its own +//! way. The `first_cut` module is the most well-developed. +//! +//! If you actually need a `Complex` type for real use, consider the +//! `num_complex` crate, whose `Complex` type is incorporated into the `num` +//! crate. - /// Imaginary portion of the complex number - im: T +macro_rules! define_complex { + () => { + #[derive(Clone, Copy, Debug)] + struct Complex { + /// Real portion of the complex number + re: T, + + /// Imaginary portion of the complex number + im: T, + } + }; } -use std::ops::Add; -use std::ops::Mul; +mod first_cut { + #[derive(Clone, Copy, Debug)] + struct Complex { + /// Real portion of the complex number + re: T, -#[cfg(skip)] -impl Add for Complex { - type Output = Complex; - fn add(self, rhs: Self) -> Self { - Complex { re: self.re + rhs.re, im: self.im + rhs.im } + /// Imaginary portion of the complex number + im: T, } -} -impl Add for Complex - where T: Add -{ - type Output = Self; - fn add(self, rhs: Self) -> Self { - Complex { re: self.re + rhs.re, im: self.im + rhs.im } + use std::ops::Add; + + impl Add for Complex + where + T: Add, + { + type Output = Self; + fn add(self, rhs: Self) -> Self { + Complex { + re: self.re + rhs.re, + im: self.im + rhs.im, + } + } } -} -#[cfg(skip)] -impl Add> for Complex - where L: Add -{ - type Output = Complex; - fn add(self, rhs: Complex) -> Self::Output { - Complex { re: self.re + rhs.re, im: self.im + rhs.im } + use std::ops::Sub; + + impl Sub for Complex + where + T: Sub, + { + type Output = Self; + fn sub(self, rhs: Self) -> Self { + Complex { + re: self.re - rhs.re, + im: self.im - rhs.im, + } + } } -} -#[cfg(skip)] -impl<'a, P, Rhs> Add for &'a Complex

- where P: Add, - Rhs: AsRef> -{ - type Output = Complex

; - fn add(self, rhs: Rhs) -> Self::Output { - let rhs = rhs.as_ref(); - Complex { re: self.re + rhs.re, im: self.im + rhs.im } + use std::ops::Mul; + + impl Mul for Complex + where + T: Clone + Add + Sub + Mul, + { + type Output = Self; + fn mul(self, rhs: Self) -> Self { + Complex { + re: self.re.clone() * rhs.re.clone() + - (self.im.clone() * rhs.im.clone()), + im: self.im * rhs.re + self.re * rhs.im, + } + } + } + + #[test] + fn try_it_out() { + let mut z = Complex { re: 1, im: 2 }; + let c = Complex { re: 3, im: 4 }; + + z = z * z + c; + + std::mem::forget(z); } -} -use std::ops::Sub; + impl PartialEq for Complex { + fn eq(&self, other: &Complex) -> bool { + self.re == other.re && self.im == other.im + } + } -impl Mul> for Complex - where T: Add + Sub + Mul + Copy -{ - type Output = Self; - fn mul(self, rhs: Self) -> Self { - Complex { re: self.re * rhs.re - self.im * rhs.im, - im: self.re * rhs.im + self.im * rhs.re } + #[test] + fn test_complex_eq() { + let x = Complex { re: 5, im: 2 }; + let y = Complex { re: 2, im: 5 }; + assert_eq!(x * y, Complex { re: 0, im: 29 }); } -} -#[test] -fn test() { - let z = Complex { re: -2, im: 6 }; - let c = Complex { re: 1, im: 2 }; - assert_eq!(z + c, Complex { re: -1, im: 8 }); - assert_eq!(z * c, Complex { re: -14, im: 2 }); - assert_eq!(z.add(c), Complex { re: -1, im: 8 }); + impl Eq for Complex {} } -#[test] -fn test_explicit() { - use std::ops::Add; +mod non_generic_add { + define_complex!(); - assert_eq!(4.125f32.add(5.75), 9.875); - assert_eq!(10.add(20), 10 + 20); -} + use std::ops::Add; -impl Add> for f64 { - type Output = Complex; - fn add(self, rhs: Complex) -> Complex { - Complex { re: rhs.re + self, im: rhs.im } + impl Add for Complex { + type Output = Complex; + fn add(self, rhs: Self) -> Self { + Complex { + re: self.re + rhs.re, + im: self.im + rhs.im, + } + } } } -#[test] -fn add_complex_to_real() { - assert_eq!(30f64 + Complex { re: 10.0f64, im: 20.0 }, - Complex { re: 40.0, im: 20.0 }); -} +mod somewhat_generic { + define_complex!(); -use std::ops::Neg; + use std::ops::Add; -impl Neg for Complex - where T: Neg -{ - type Output = Complex; - fn neg(self) -> Complex { - Complex { re: -self.re, im: -self.im } + impl Add for Complex + where + T: Add, + { + type Output = Self; + fn add(self, rhs: Self) -> Self { + Complex { + re: self.re + rhs.re, + im: self.im + rhs.im, + } + } } -} -#[test] -fn negate_complex() { - let z = Complex { re: 3, im: 4 }; - assert_eq!(-z, Complex { re: -3, im: -4 }); + use std::ops::Neg; + + impl Neg for Complex + where + T: Neg, + { + type Output = Complex; + fn neg(self) -> Complex { + Complex { + re: -self.re, + im: -self.im, + } + } + } } -use std::ops::AddAssign; +mod very_generic { + define_complex!(); + + use std::ops::Add; -impl AddAssign for Complex - where T: AddAssign -{ - fn add_assign(&mut self, rhs: Complex) { - self.re += rhs.re; - self.im += rhs.im; + impl Add> for Complex + where + L: Add, + { + type Output = Complex; + fn add(self, rhs: Complex) -> Self::Output { + Complex { + re: self.re + rhs.re, + im: self.im + rhs.im, + } + } } } -#[test] -fn compound_assignment() { - let mut z = Complex { re: 5, im: 6 }; - z += Complex { re: 7, im: 8 }; - assert_eq!(z, Complex { re: 12, im: 14 }); +mod impl_compound { + define_complex!(); + + use std::ops::AddAssign; - let mut title = "Love".to_string(); - title += ", Actually"; - assert_eq!(title, "Love, Actually"); + impl AddAssign for Complex + where + T: AddAssign, + { + fn add_assign(&mut self, rhs: Complex) { + self.re += rhs.re; + self.im += rhs.im; + } + } } -impl PartialEq for Complex { - fn eq(&self, other: &Complex) -> bool { - self.re == other.re && self.im == other.im +mod derive_partialeq { + #[derive(Clone, Copy, Debug, PartialEq)] + struct Complex { + re: T, + im: T, } } -impl Eq for Complex { } +mod derive_everything { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + struct Complex { + /// Real portion of the complex number + re: T, -#[test] -fn comparison() { - let x = Complex { re: 5, im: 2 }; - let y = Complex { re: 2, im: 5 }; - assert_eq!(x * y, Complex { re: 0, im: 29 }); + /// Imaginary portion of the complex number + im: T, + } } -use std::fmt; +/// Examples from Chapter 17, Strings and Text +/// +/// These use a separate, non-generic `Complex` type, for simplicity. +mod formatting { + #[test] + fn complex() { + #[derive(Copy, Clone, Debug)] + struct Complex { re: f64, im: f64 } -// To make the formatting examples mesh with the rest of this file, I've adapted -// them to work on the type `Complex`, where the book simply defines a new -// non-generic `Complex` type. The only changes are adding ``, and changing -// the field names. + let third = Complex { re: -0.5, im: f64::sqrt(0.75) }; + println!("{:?}", third); -#[cfg(skip)] -impl fmt::Display for Complex { - fn fmt(&self, dest: &mut fmt::Formatter) -> fmt::Result { - let i_sign = if self.i < 0.0 { '-' } else { '+' }; - write!(dest, "{} {} {}i", self.r, i_sign, f64::abs(self.i)) - } -} + use std::fmt; -impl fmt::Display for Complex { - fn fmt(&self, dest: &mut fmt::Formatter) -> fmt::Result { - let (r, i) = (self.re, self.im); - if dest.alternate() { - let abs = f64::sqrt(r * r + i * i); - let angle = f64::atan2(i, r) / std::f64::consts::PI * 180.0; - write!(dest, "{} ∠ {}°", abs, angle) - } else { - let i_sign = if i < 0.0 { '-' } else { '+' }; - write!(dest, "{} {} {}i", r, i_sign, f64::abs(i)) + impl fmt::Display for Complex { + fn fmt(&self, dest: &mut fmt::Formatter) -> fmt::Result { + let im_sign = if self.im < 0.0 { '-' } else { '+' }; + write!(dest, "{} {} {}i", self.re, im_sign, f64::abs(self.im)) + } } + + let one_twenty = Complex { re: -0.5, im: 0.866 }; + assert_eq!(format!("{}", one_twenty), + "-0.5 + 0.866i"); + + let two_forty = Complex { re: -0.5, im: -0.866 }; + assert_eq!(format!("{}", two_forty), + "-0.5 - 0.866i"); } -} -#[test] -fn custom_display_impl() { - let one_twenty = Complex { re: -0.5, im: 0.866 }; - assert_eq!(format!("{}", one_twenty), - "-0.5 + 0.866i"); - - let two_forty = Complex { re: -0.5, im: -0.866 }; - assert_eq!(format!("{}", two_forty), - "-0.5 - 0.866i"); - - let ninety = Complex { re: 0.0, im: 2.0 }; - assert_eq!(format!("{}", ninety), - "0 + 2i"); - assert_eq!(format!("{:#}", ninety), - "2 ∠ 90°"); + #[test] + fn complex_fancy() { + #[derive(Copy, Clone, Debug)] + struct Complex { re: f64, im: f64 } + + use std::fmt; + + impl fmt::Display for Complex { + fn fmt(&self, dest: &mut fmt::Formatter) -> fmt::Result { + let (re, im) = (self.re, self.im); + if dest.alternate() { + let abs = f64::sqrt(re * re + im * im); + let angle = f64::atan2(im, re) / std::f64::consts::PI * 180.0; + write!(dest, "{} ∠ {}°", abs, angle) + } else { + let im_sign = if im < 0.0 { '-' } else { '+' }; + write!(dest, "{} {} {}i", re, im_sign, f64::abs(im)) + } + } + } + + let ninety = Complex { re: 0.0, im: 2.0 }; + assert_eq!(format!("{}", ninety), + "0 + 2i"); + assert_eq!(format!("{:#}", ninety), + "2 ∠ 90°"); + } }