rustc main.rs //编译
./main 运行二进制文件
普通注释:
//
/**/
文档注释
/// 为接下来的项生成帮助文档
//!为注释所属的项生成帮助文档
{} 在输出中充当占位符
由std::fmt中的fmt宏处理
fn main() {
println!("{} days", 31i32); //居然可以加后缀
println!("{0},this is {1}. {1},this is {0}", "Alice", "Bob"); //要打印的值是有索引的
println!("{subject}", subject = "the lazy dog"); //可以使用命名参数
println!("{} of {:b}", 1, 16); //十进制转二进制
}
所有 std 库类型都天生可以使用 {:?} 来打印:
#[derive(Debug)] //所有类型加上debug都能自动为其实现Debug trait,fmt::Display 则需要手动实现
struct DebugPrintable(String); //单元组结构体,牛
#[derive(Debug)]
struct Deep(DebugPrintable); //还可以嵌套打印
fn main() {
let de = Deep(DebugPrintable(String::from("shiyivei")));
println!("{:?}", de);
println!("{0:?}", Deep(DebugPrintable(String::from("shiyivei")))); //可以直接传值打印
println!("{0:#?}", Deep(DebugPrintable(String::from("yivei")))); //要想变得更美,用#,它就会给你打印成结构体
}
use std::fmt;
#[derive(Debug)]
struct Structure(i32); //单元组结构体,牛
impl fmt::Display for Structure {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
fn main() {
println!("{}", Structure(10)); //只会打印某个值
println!("{:#?}", Structure(20)); //会打印整个结构体
}
?操作符
write!(f, "{}", value)?; //如果有错误就返回错误,没有错误就继续执行
use std::fmt;
#[derive(Debug)]
struct List(Vec); //单元组结构体,牛
impl fmt::Display for List {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let vec = &self.0;
write!(f, "[")?;
for (count, v) in vec.iter().enumerate() {
if count != 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, "]")
}
}
fn main() {
let v = List(vec![1, 2, 3]); //有时候为了美观需要为类型实现display trait
println!("{}", v); //只会打印某个值
println!("{:#?}", List(vec![1, 2, 3])); //会打印整个结构体
}
标量类型有两个特殊
char(字符):单个 Unicode 字符,如 'a','α' 和 '∞'(每个都是 4 字节)
单元类型(unit type):()。其唯一可能的值就是 () 这个空元组
复合类型
数组(array):如 [1, 2, 3]
元组(tuple):如 (1, true)
前缀 0x、0o、0b,数字可以用十六进制、八进制或二进制记法表示
在数值字面量中插入下划线,比如:1_000 等同于 1000,0.000_001 等同于 0.000001
可以在字面量后面加上类型告诉编译器字面量的类型:如10i32
println!("1 - 2 = {}", 1i32 - 2); //类型决定了它的操作范围
println!("1 * 2^5 = {}", 1u32 << 5); //位运算
元组可以当函数参数和返回值
fn reverse(pair: (i32, bool)) -> (bool, i32) {
let (integer, boolean) = pair;
(boolean, integer) //最后一个语句不要加 ;
}
let (a, b) = reverse((12, true));
println!("{},{}", a, b);
let tuple = reverse((12, true));
println!("{:?}", tuple);
struct Matrix(f32, f32, f32, f32); //结构体元组,元组也可以套娃,可以解构
和Go语言中的含义一样
let xs: [i32; 5] = [1, 2, 3, 4, 5]; 数组,存储在栈上
fn analyze_slice(slice: &[i32]) { //引用指的是给栈上的数据再创建一个指针,存放在栈上,引用停止时,它指向的栈上数据并不会被丢弃,只会丢弃引用本身这个指针,创建一个引用的行为就是借用,借用可以更改可变值,但是同一时间只能有一个可变借用,引用变量也可以是变得或者不可变的
println!("first element of the slice: {}", slice[0]);
println!("the slice has {} elements", slice.len());
}
rust有两种自定义类型:结构体和枚举
元组结构体:实际上就是具名元组
单元结构体:不带字段,在泛型中很有用
常规结构体
#[derive(Debug)]
struct Uint(i32);
#[derive(Debug)]
struct Pair(i32, i32);
#[derive(Debug)]
#[allow(dead_code)] // 该属性用于隐藏对未使用代码的警告。
struct Point {
x: i32,
y: i32,
}
fn main() {
let _uint = Uint(-9);
let pair = Pair(1, 2);
let point = Point { x: 3, y: 4 };
println!("{:#?},{:?},{:?}", _uint, pair, point);
}
枚举
枚举操作方式和结构体操作方式相同,但是实例化一个枚举只需要实现枚举中某一个实例即可,而结构体都要实现
enum WebEvent {
PageLoad,//单元结构体
PageUnload,
KeyPress(char),//元组结构体
Paste(String),
Click { x: i64, y: i64 },
}
fn inspect(event: WebEvent) {
match event { //枚举常和match结合使用
WebEvent::PageLoad => println!("page load"),
WebEvent::PageUnload => println!("page unload"),
WebEvent::KeyPress(c) => println!(" pressed {}", c),
WebEvent::Paste(s) => println!("pasted \"{}\"", s),
WebEvent::Click { x, y } => println!("click at x={},y={}", x, y),
}
}
fn main() {
let load = WebEvent::PageLoad;
let un_load = WebEvent::PageUnload;
let press = WebEvent::KeyPress('x');
let paste = WebEvent::Paste("my_text".to_owned());
let click = WebEvent::Click { x: 20, y: 80 };
inspect(load);
inspect(un_load);
inspect(press);
inspect(paste);
inspect(click);
}
类型别名
enum ThisEnumHasALongName {
Add,
Subject,
}
type Name = ThisEnumHasALongName;//重给起一个名字;let x = Name::Add;,最常见的就是在impl块中使用self
使用use就可以不使用变量/函数的完整路径了。use可以用在任何地方,代码文件开头或者函数体内部
#[derive(Debug)]
#[allow(dead_code)]
//隐式辨别值,跟智能的一样,Rust的灵活性确实非常的富有表现能力
enum Number {
Zero,
One,
Two,
}
//显式辨别值
#[allow(dead_code)]
enum Color {
Red = 0xff0000,
Green = 0x00ff00,
Blue = 0x0000ff,
}
fn main() {
println!("zero is {}", Number::Zero as i32);
println!("zero is {}", Number::Two as i32);
println!("Red is {}", Color::Red as i32);
println!("Green is {}", Color::Green as i32);
}
use List::*;
enum List {
Cons(u32, Box),
Nil,
}
impl List {
fn new() -> List {
Nil
}
fn prepend(self, elem: u32) -> List {
Cons(elem, Box::new(self))
}
#[allow(dead_code)]
fn len(&self) -> u32 {
match *self {
Cons(_head, ref tail) => 1 + tail.len(),
Nil => 0,
}
}
fn stringify(&self) -> String {
match *self {
Cons(head, ref tail) => { //这个ref相当于给等号右边的值加&
format!("{},{}", head, tail.stringify())
}
Nil => {
format!("Nil")
}
}
}
}
fn main() {
let mut list = List::new();
list = list.prepend(1);
list = list.prepend(2);
list = list.prepend(3);
println!("{}", list.stringify())
}
const TEST: i32 = 10;
static TESTAGAIN: &'static str = "Rust";
Rust通过静态类型来保证类型安全
let uint = ();
println!("{:?}", uint);
加上 mut
暂不建议
可变变量被不变变量绑定时,在未出不变变量的作用域之前,该值变量会被冻结
Rust 不提供原生类型之间的隐式类型转换,使用as关键字进行显式类型转换
fn main() {
let decimal = 49.4;
let integer = decimal as u8;
let chare = integer as char;
println!("{},{},{}", decimal, integer, chare);
println!("{}", 1000 as u16)
}
字面量默认使用i32和f64,但是可以在其后增加类型如
let num = 20i64
编译很聪明,大多数情况下不需要写类型声明
可以给任何一个类型起别名,而不仅仅限于自定义类型
type accountId = u32;
type IoResult = Result;
Rust使用trait来解决类型转换的问题,一般用到的是From和Into两个trait。但是也可能用到其他trait,尤其是关于String的转换
From
use std::convert::From;
#[derive(Debug)]
struct Num {
value: i32,
}
impl From for Num { //自已实现一个from
fn from(item: i32) -> Self {
Num { value: item }
}
}
Into就是把From倒过来
use std::convert::From;
#[derive(Debug)]
struct Num {
value: i32,
}
impl From for Num {
fn from(item: i32) -> Self {
Num { value: item }
}
}
fn main() {
let int = 5;
let num: Num = int.into();
println!("{:?}", num);
}
TryFrom
和 TryInto
traitTryFrom
和 TryInto
trait 用于易出错的转换
use std::convert::TryFrom;
use std::convert::TryInto;
#[derive(Debug, PartialEq)]
struct EventNum(i32);
impl TryFrom for EventNum {
type Error = ();
fn try_from(value: i32) -> Result {
if value % 2 == 0 {
Ok(EventNum(value))
} else {
Err(())
}
}
}
fn main() {
assert_eq!(EventNum::try_from(8), Ok(EventNum(8)));
assert_eq!(EventNum::try_from(5), Err(()));
let result: Result = 8i32.try_into();
assert_eq!(result, Ok(EventNum(8)))
}
要把任何类型转为String,请先实现Display trait,还多了一个打印的特性
这个和之前我们在打印那一节实现是一样的
use std::fmt;
use std::string::ToString;
struct Circle {
radius: i32,
}
impl fmt::Display for Circle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Circle of radius {}", self.radius)
}
}
fn main() {
let circle = Circle { radius: 10 };
println!("{}", circle.radius.to_string());
}
使用parse
let parse: u32 = "5".parse().unwrap();
let turbo_parsed = "10".parse::().unwrap(); //涡轮鱼法,好奇怪的名字
println!("{},{}", parse + 10, turbo_parsed + 21);
只需要为目标类型(自定义的)实现 FromStr
trait 即可,标准库中为无数类型实现了FromStr trait
let f = "23.8";
let my_f = f.parse::().unwrap();
println!("{}", my_f);
带分号的都是表达式
每个条件后面都跟一个{}代码块
fn main() {
let mut count = 0u32;
println!("let us count until infinity");
loop {
count += 1;
if count == 3 {
println!("three");
continue;
}
if count == 6 {
println!("ok, that's enough");
break;
}
}
}
fn main() {
'outer: loop {
println!("entry the outer loop");
'inner: loop {
println!("entry the inner loop");
// break 'outer;
break;
}
println!("this point will never be reached");
break;
}
println!("exit the outer loop")
}
fn main() {
let mut count = 0;
let result = loop {
count += 1;
if count == 10 {
break count * 2;
}
};
assert_eq!(result, 20) //在常规代码中,也可以使用断言语句
}
一种循环结构的是for…in
fn main() {
for i in 0..101 { //要包含右端点的可以选择 0..=101
if i % 15 == 0 {
println!("15")
} else if i % 5 == 0 {
println!("5")
} else if i % 3 == 0 {
println!("3")
} else {
println!("i")
}
}
}
for和迭代器
iter 借用
fn main() {
let names = vec!["frank", "bob", "ferries"];
for name in names.iter() { //借用集合中的元素,遍历完原集合可以继续使用
match name {
&"ferries" => println!("{} is good at rust", name),
_ => println!("hello {}", name),
}
}
}
Into_iter 消耗
fn main() {
let names = vec!["frank", "bob", "ferries"];
for name in names.into_iter() {
//消耗集合中的元素,遍历完原集合可以继续使用
match name {
"ferries" => println!("{} is good at rust", name),
_ => println!("hello {}", name),
}
}
}
Iter_mut 就地改变
fn main() {
let mut names = vec!["frank", "bob", "ferries"];
for name in names.iter_mut() {
//改变集合中的元素
*name = match name {
&mut "ferries" => "hello ferries", //直接更改,这一点比go语言要方便,go语言只有一种方式,就是通过索引
_ => "hello",
}
}
println!("{:?}", names)
}
与c语言中的switch相似
这个没什么可以说的,分支必须覆盖所有可能
fn main() {
let boolean = true;
let library = match boolean {
true => 0,
false => 1,
};
}
fn main() {
let triple = (1, 1, 2);
println!("{:?}", triple);
match triple {
(0, y, z) => println!("{},{},{}", 0, y, z), //解构两个
(1, x, ..) => println!("{},{}", 1, x), //只解构第二个
_ => println!("others"),
}
}
fn main() {
let color = Color::RGB(127, 40, 17); //注意枚举实例的构造
match color {
Color::Red => println!("The color is red"),
Color::Blue => println!("The color is blue"),
Color::Green => println!("The color is green"),
Color::RGB(r, g, b) => println!("Red {},Green {},Blue {}", r, g, b),
Color::HSV(r, g, b) => println!("Red {},Green {},Blue {}", r, g, b),
Color::CMY(r, g, b) => println!("Red {},Green {},Blue {}", r, g, b),
Color::HSL(r, g, b) => println!("Red {},Green {},Blue {}", r, g, b),
Color::CMYK(r, g, b, k) => println!("Red {},Green {},Blue {}", r, g, b),
}
}
指针和引用
对于指针来说,解构和解引用要分开
解引用使用: *
解构使用:&、ref 和 mut
在等号左边创建引用一
let ref x = 3;
let ref mut y = "non";
在右边创建引用
let refe = &4;
fn main() {
let refe = &4;
match refe {
&val => println!("the value is {:?}", val), //val这个名字随意命名
}
match *refe {
val => println!("the value is {:?}", val), //两种方式
}
let ref x = 3; //获得引用
let mut y = 7;
match x {
ref x => println!("{}", x),
}
match y {
ref mut m => {
//获得了引用
*m += 10;
println!("{}", m) //解应用后才能更改值
}
}
}
fn main() {
let foo = Foo { x: (1, 2), y: 3 }; //先实例化,再解构
let Foo { x: (x, y), y: z } = foo; //解构和赋值有点像,只不过在=左边,并且值用变量名称代替,右边是实例
println!("{},{},{},{}", x, y, z, foo.y); //这样就可以一个一个打印出来了,当然如果不解构,也是可以打印的
}
就是在match分支语句值后面加上条件语句
fn main() {
let pair = (2, -2);
match pair {
(x, y) if x + y == 0 => println!("a = -b"),
_ => println!("a != -b"),
}
}
使用@
fn main() {
match age() {
0 => println!("not born"),
n @ 1..=12 => println!("child"), //间接访问变量比如函数体内部的值,使用@重新绑定
n @ 13..=19 => println!("young man"),
_ => println!("old man"),
}
}
fn age() -> u32 {
15
}
可以使用绑定来解构 enum变体
fn main() {
match some_number() {
Some(n @ 42) => println!("The answer is {}", n),
_ => println!("old man"),
}
}
fn some_number() -> Option {
Some(42)
}
只取想要的值,其他都用else 分支来处理
fn main() {
let number = Some(7);
let letter: Option = None;
let emotion: Option = None;
if let Some(i) = letter {
println!("{}", i)
} else {
println!("nothing match")
}
}
匹配枚举
enum Foo {Bar}
fn main() {
let a = Foo::Bar;
// 变量匹配 Foo::Bar
if let Foo::Bar = a {
// ^-- 这就是编译时发现的错误。使用 `if let` 来替换它。
println!("a is foobar");
}
}
if let 和 while let都可以把match 分支改的好看