basic
Macro
macro_rules! println {
() => { ... };
($($arg:tt)*) => { ... };
}
//============================
#![allow(unused)]
fn main() {
println!(); // prints just a newline
println!("hello there!");
println!("format {} arguments", "some");
let local_variable = "some";
println!("format {local_variable} arguments");
}
macro_rules! format {
($($arg:tt)*) => { ... };
}
//====================================
format!("test"); // => "test"
format!("hello {}", "world!"); // => "hello world!"
format!("x = {}, y = {val}", 10, val = 30); // => "x = 10, y = 30"
let (x, y) = (1, 2);
format!("{x} + {y} = 3"); // => "1 + 2 = 3"
macro_rules! assert {
($cond:expr $(,)?) => { ... };
($cond:expr, $($arg:tt)+) => { ... };
}
//====================================
assert!(true);
fn some_computation() -> bool { true } // a very simple function
assert!(some_computation());
// assert with a custom message
let x = true;
assert!(x, "x wasn't true!");
let a = 3; let b = 27;
assert!(a + b == 30, "a = {}, b = {}", a, b);
Memory Layout
s slice
+---------+--------+----------+ +---------+--------+
Stack | pointer | length | capacity | | pointer | length |
| | | 5 | 5 | | | | 4 |
+----|----+--------+----------+ +----|----+--------+
| s |
| |
v |
+---+---+---+---+---+ |
Heap: | H | e | l | l | o | |
+---+---+---+---+---+ |
^ |
| |
+--------------------------------+
Control Flow
Loop
Derive macros
Generic programming
fn print_if_even<T>(n: T) //(1)
where
T: IsEven + Debug //(2)
{
if n.is_even() {
println!("{n:?} is even");
}
}
- 参数n的类型是T
- 这里的冒号意味着约束,约束为类型T必须实现某个trait
Two different trait bounds
Generics and associated types
Example
pub trait From<T> { //(1)
fn from(value: T) -> Self;
}
pub trait Deref {
type Target; //(2)
fn deref(&self) -> &Self::Target;
}
- from 实现了泛型
- deref 实现了关联类型
关联类型,对于一种struct ,只可能有一种实现 而对于泛型,在impl时可以传入多种不同的参数
An associated type is uniquely determined by the trait implementation. you'll only be able to specify one Target for a given type and there won't be any ambiguity.
尖括号<>
复合类型(Compound Types)
Trait
🌟 The trait is defined in the current crate
🌟 The implementor type is defined in the current crate
Operator
pub trait Add<Rhs = Self> {
type Output;
// Required method
fn add(self, rhs: Rhs) -> Self::Output;
}
//==========================
use std::ops::Add;
#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
impl Add for Point {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
assert_eq!(Point { x: 1, y: 0 } + Point { x: 2, y: 3 },
Point { x: 3, y: 3 });
pub trait Sub<Rhs = Self> {
type Output;
// Required method
fn sub(self, rhs: Rhs) -> Self::Output;
}
//============================================
use std::ops::Sub;
#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
impl Sub for Point {
type Output = Self;
fn sub(self, other: Self) -> Self::Output {
Self {
x: self.x - other.x,
y: self.y - other.y,
}
}
}
assert_eq!(Point { x: 3, y: 3 } - Point { x: 2, y: 3 },
Point { x: 1, y: 0 });
pub trait Mul<Rhs = Self> {
type Output;
// Required method
fn mul(self, rhs: Rhs) -> Self::Output;
}
//==========================
use std::ops::Mul;
// By the fundamental theorem of arithmetic, rational numbers in lowest
// terms are unique. So, by keeping `Rational`s in reduced form, we can
// derive `Eq` and `PartialEq`.
#[derive(Debug, Eq, PartialEq)]
struct Rational {
numerator: usize,
denominator: usize,
}
impl Rational {
fn new(numerator: usize, denominator: usize) -> Self {
if denominator == 0 {
panic!("Zero is an invalid denominator!");
}
// Reduce to lowest terms by dividing by the greatest common
// divisor.
let gcd = gcd(numerator, denominator);
Self {
numerator: numerator / gcd,
denominator: denominator / gcd,
}
}
}
impl Mul for Rational {
// The multiplication of rational numbers is a closed operation.
type Output = Self;
fn mul(self, rhs: Self) -> Self {
let numerator = self.numerator * rhs.numerator;
let denominator = self.denominator * rhs.denominator;
Self::new(numerator, denominator)
}
}
// Euclid's two-thousand-year-old algorithm for finding the greatest common
// divisor.
fn gcd(x: usize, y: usize) -> usize {
let mut x = x;
let mut y = y;
while y != 0 {
let t = y;
y = x % y;
x = t;
}
x
}
assert_eq!(Rational::new(1, 2), Rational::new(2, 4));
assert_eq!(Rational::new(2, 3) * Rational::new(3, 4),
Rational::new(1, 2));
pub trait Div<Rhs = Self> {
type Output;
// Required method
fn div(self, rhs: Rhs) -> Self::Output;
}
//========
use std::ops::Div;
// By the fundamental theorem of arithmetic, rational numbers in lowest
// terms are unique. So, by keeping `Rational`s in reduced form, we can
// derive `Eq` and `PartialEq`.
#[derive(Debug, Eq, PartialEq)]
struct Rational {
numerator: usize,
denominator: usize,
}
impl Rational {
fn new(numerator: usize, denominator: usize) -> Self {
if denominator == 0 {
panic!("Zero is an invalid denominator!");
}
// Reduce to lowest terms by dividing by the greatest common
// divisor.
let gcd = gcd(numerator, denominator);
Self {
numerator: numerator / gcd,
denominator: denominator / gcd,
}
}
}
impl Div for Rational {
// The division of rational numbers is a closed operation.
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
if rhs.numerator == 0 {
panic!("Cannot divide by zero-valued `Rational`!");
}
let numerator = self.numerator * rhs.denominator;
let denominator = self.denominator * rhs.numerator;
Self::new(numerator, denominator)
}
}
// Euclid's two-thousand-year-old algorithm for finding the greatest common
// divisor.
fn gcd(x: usize, y: usize) -> usize {
let mut x = x;
let mut y = y;
while y != 0 {
let t = y;
y = x % y;
x = t;
}
x
}
assert_eq!(Rational::new(1, 2), Rational::new(2, 4));
assert_eq!(Rational::new(1, 2) / Rational::new(3, 4),
Rational::new(2, 3));
pub trait Rem<Rhs = Self> {
type Output;
// Required method
fn rem(self, rhs: Rhs) -> Self::Output;
}
//================================
use std::ops::Rem;
#[derive(PartialEq, Debug)]
struct SplitSlice<'a, T> {
slice: &'a [T],
}
impl<'a, T> Rem<usize> for SplitSlice<'a, T> {
type Output = Self;
fn rem(self, modulus: usize) -> Self::Output {
let len = self.slice.len();
let rem = len % modulus;
let start = len - rem;
Self {slice: &self.slice[start..]}
}
}
// If we were to divide &[0, 1, 2, 3, 4, 5, 6, 7] into slices of size 3,
// the remainder would be &[6, 7].
assert_eq!(SplitSlice { slice: &[0, 1, 2, 3, 4, 5, 6, 7] } % 3,
SplitSlice { slice: &[6, 7] });
pub trait PartialEq {
// Required method
//
// `Self` is a Rust keyword that stands for
// "the type that is implementing the trait"
fn eq(&self, other: &Self) -> bool;
// Provided method
fn ne(&self, other: &Self) -> bool { ... } //(1)
}
//====================
use std::ops::Add;
#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
impl Add for Point {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
assert_eq!(Point { x: 1, y: 0 } + Point { x: 2, y: 3 },
Point { x: 3, y: 3 });
- fn ne is implmented by default
pub trait PartialOrd<Rhs = Self>: PartialEq<Rhs>
where
Rhs: ?Sized,
{
// Required method
fn partial_cmp(&self, other: &Rhs) -> Option<Ordering>; //(1)
// Provided methods
fn lt(&self, other: &Rhs) -> bool { ... }
fn le(&self, other: &Rhs) -> bool { ... }
fn gt(&self, other: &Rhs) -> bool { ... }
fn ge(&self, other: &Rhs) -> bool { ... }
}
//
- the only one need to be done
Other
//std::ops
pub trait Deref {
type Target: ?Sized;
// Required method
fn deref(&self) -> &Self::Target;
}
//==========================
use std::ops::Deref;
struct DerefExample<T> {
value: T
}
impl<T> Deref for DerefExample<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
let x = DerefExample { value: 'a' };
assert_eq!('a', *x);
- no methods to implement just means that it is a flag
如果一个结构体是泛型的,那么它会假定结构体所包含的类型T实现了Size 的trait
pub trait From<T>: Sized {
// Required method
fn from(value: T) -> Self;
}
//============================
use std::fs;
use std::io;
use std::num;
enum CliError {
IoError(io::Error),
ParseError(num::ParseIntError),
}
impl From<io::Error> for CliError {
fn from(error: io::Error) -> Self {
CliError::IoError(error)
}
}
impl From<num::ParseIntError> for CliError {
fn from(error: num::ParseIntError) -> Self {
CliError::ParseError(error)
}
}
fn open_and_parse_file(file_name: &str) -> Result<i32, CliError> {
let mut contents = fs::read_to_string(&file_name)?;
let num: i32 = contents.trim().parse()?;
Ok(num)
}
From实现后,将自动生成一个Into的方法, 在这里sized 是约束From 的trait ,也就是约束self,意味着必须实现了sized trait的类型才能实现sized , super trait??
pub trait Drop {
// Required method
fn drop(&mut self);
}
//=======================
struct HasDrop;
impl Drop for HasDrop {
fn drop(&mut self) {
println!("Dropping HasDrop!");
}
}
struct HasTwoDrops {
one: HasDrop,
two: HasDrop,
}
impl Drop for HasTwoDrops {
fn drop(&mut self) {
println!("Dropping HasTwoDrops!");
}
}
fn main() {
let _x = HasTwoDrops { one: HasDrop, two: HasDrop };
println!("Running!");
}
//output =========
Running!
Dropping HasTwoDrops!
Dropping HasDrop!
Dropping HasDrop!
- 对于 Rust 的堆内存来说,即使没有显式地实现 Drop trait,Rust 的所有权系统仍然会在对象离开作用域时自动释放它所拥有的内存资源,因此不会发生内存泄漏。
- 但是,如果你的类型使用了除了堆内存以外的外部资源,比如打开文件、网络连接或者其他需要手动释放的资源,而没有通过实现 Drop trait 来释放这些资源,那么这些资源可能不会被正确清理,从而导致资源泄漏。