编辑:张汉东
返回多态类型
use rand::{thread_rng, Rng};/// This is the trait that every die needs to implement to be... well... "rollable", right?pub trait Rollable {/// Roll the diefn roll() -> Self;/// Get the value from the latest rollfn val(&self) -> u8;}/// A generic function to roll a given die.pub fn roll<T: Rollable>() -> T {Rollable::roll() // <- Note that here `Rollable` is the current type for a given call!}/// A D6 die (6 faces): a roll will give you a `u8` in the `1..=6` range.#[derive(Debug)]pub struct D6(u8);impl Rollable for D6 {fn roll() -> D6 {D6 {0: thread_rng().gen_range(1..=6),}}fn val(&self) -> u8 {self.0}}/// A D8 die (8 faces): a roll will give you a `u8` in the `1..=8` range.#[derive(Debug)]pub struct D8(u8);impl Rollable for D8 {fn roll() -> D8 {D8 {0: thread_rng().gen_range(1..=8),}}fn val(&self) -> u8 {self.0}}#[derive(Debug)]struct Fake100(u8);impl Rollable for Fake100 {fn roll() -> Fake100 {Fake100 { 0: 100 } // <- forces it to roll 100}fn val(&self) -> u8 {self.0}}fn main() {// let's roll a D6let r: D6 = roll();println!("{:?}", r); // D6(3)// let's roll a D8let r: D8 = roll();println!("{:?}", r); // D8(3)println!("I bet I'll get a 100 this time!");let d: Fake100 = roll();println!("Look what I got: {}!", d.val()) // <- yeah this will always be 100}
也支持类型推断:
fn try_dodge_attack(d6: D6, d8: D8) -> bool {d6.val() + d8.val() > 10}fn main() {let escaped = try_dodge_attack(roll(), roll());println!("{}",match escaped {true => "You dogded!",false => "Ouch! The attack hit you!",});}
一个零开销链表的实现
下面代码实现了一个 持久性/不变性(Persistent / Immutable )的单向链表(Singly-linked)。
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]pub enum List<'a, T> {Node { data: T, next: &'a List<'a, T> },Tail,}impl<T> Default for List<'_, T> {fn default() -> Self {List::Tail}}impl<'a, T> List<'a, T> {pub fn add(&'a self, data: T) -> Self {List::Node { data, next: self }}pub fn rev_iter(&'a self, f: impl Fn(&'a T)) {if let List::Node { data, next } = self {next.rev_iter(&f);f(data);}}pub fn try_rev_iter<E, F>(&'a self, f: F) -> Result<(), E>whereF: Fn(&'a T) -> Result<(), E>,{if let List::Node { data, next } = self {next.try_rev_iter(&f)?;f(data)?;}Ok(())}}pub struct ListIter<'a, T>(&'a List<'a, T>);impl<'a, T> IntoIterator for &'a List<'a, T> {type Item = &'a T;type IntoIter = ListIter<'a, T>;fn into_iter(self) -> Self::IntoIter {ListIter(self)}}impl<'a, T> Iterator for ListIter<'a, T> {type Item = &'a T;fn next(&mut self) -> Option<Self::Item> {match self.0 {List::Node { data, next } => {self.0 = next;Some(data)}List::Tail => None,}}}#[derive(Debug, Clone, PartialEq)]pub enum Value {Num(f64),Bool(bool),String(String),}#[derive(PartialEq)]pub enum ValueKind {Num,Bool,String,}impl Value {pub fn kind(&self) -> ValueKind {match self {Value::Num(_) => ValueKind::Num,Value::Bool(_) => ValueKind::Bool,Value::String(_) => ValueKind::String,}}}#[derive(Debug, Clone)]pub enum Expr {Value(Value),Variable(String),UnExpr(UnExprKind, Box<Expr>),BinExpr(BinExprKind, Box<(Expr, Expr)>),Define(String, Box<(Expr, Expr)>),IfThenElse(Box<(Expr, Expr, Expr)>),}#[derive(Debug, Copy, Clone)]pub enum UnExprKind {Not,Neg,}#[derive(Debug, PartialEq, Copy, Clone)]pub enum BinExprKind {// ArithmeticAdd,Sub,Mul,Div,// LogicAnd,Or,Equals,NotEquals,}type Variables<'a> = List<'a, (String, Value)>;pub fn eval(vars: &Variables<'_>, expr: Expr) -> Option<Value> {match expr {Expr::Value(val) => Some(val),Expr::Variable(var) => vars.into_iter().find(|&(v, _)| *v == var).map(|(_, val)| val.clone()),Expr::UnExpr(kind, expr) => {eval_unary(kind, vars, *expr)}Expr::BinExpr(kind, exprs) => {eval_binary(kind, vars, exprs.0, exprs.1)}Expr::Define(name, exprs) => {let value = eval(vars, exprs.0)?;let vars = vars.add((name, value));eval(&vars, exprs.1)}Expr::IfThenElse(exprs) => {if let Value::Bool(b) = eval(vars, exprs.0)? {eval(vars, if b { exprs.1 } else { exprs.2 })} else {None}}}}fn eval_unary(kind: UnExprKind,vars: &Variables<'_>,expr: Expr,) -> Option<Value> {let val = eval(vars, expr)?;match (kind, val) {(UnExprKind::Not, Value::Bool(b)) => {Some(Value::Bool(!b))}(UnExprKind::Neg, Value::Num(n)) => Some(Value::Num(-n)),_ => None,}}fn eval_binary(kind: BinExprKind,vars: &Variables<'_>,lhs: Expr,rhs: Expr,) -> Option<Value> {let lhs = eval(vars, lhs)?;match kind {BinExprKind::Add => {if let Value::Num(lhs) = lhs {if let Value::Num(rhs) = eval(vars, rhs)? {return Some(Value::Num(lhs + rhs));}}None}BinExprKind::Sub => {if let Value::Num(lhs) = lhs {if let Value::Num(rhs) = eval(vars, rhs)? {return Some(Value::Num(lhs - rhs));}}None}BinExprKind::Mul => {if let Value::Num(lhs) = lhs {if let Value::Num(rhs) = eval(vars, rhs)? {return Some(Value::Num(lhs * rhs));}}None}BinExprKind::Div => {if let Value::Num(lhs) = lhs {if let Value::Num(rhs) = eval(vars, rhs)? {return Some(Value::Num(lhs / rhs));}}None}BinExprKind::And => {if let Value::Bool(lhs) = lhs {if !lhs {return Some(Value::Bool(false));}if let Value::Bool(rhs) = eval(vars, rhs)? {return Some(Value::Bool(rhs));}}None}BinExprKind::Or => {if let Value::Bool(lhs) = lhs {if lhs {return Some(Value::Bool(true));}if let Value::Bool(rhs) = eval(vars, rhs)? {return Some(Value::Bool(rhs));}}None}BinExprKind::Equals => {let rhs = eval(vars, rhs)?;if lhs.kind() == rhs.kind() {Some(Value::Bool(lhs == rhs))} else {None}}BinExprKind::NotEquals => {let rhs = eval(vars, rhs)?;if lhs.kind() == rhs.kind() {Some(Value::Bool(lhs != rhs))} else {None}}}}
