提交 2f0f026d 编写于 作者: 梦境迷离

add rust

上级 15a4e74b
......@@ -10,3 +10,6 @@ edition = "2018"
#新增随机数支持依赖,该数字0.5.5实际上是的简写^0.5.5,表示“具有与0.5.5版兼容的公共API的任何版本。”
rand = "0.5.5"
[profile.release]
panic = 'abort' #发布模式下因恐慌(panic,不可恢复的错误,类似Java的error)而中止,通过将RUST_BACKTRACE环境变量设置为除0以外的任何值来获得错误的确切原因的回溯
use std::string::ToString;
///https://doc.rust-lang.org/stable/nomicon/vec.html.
///集合
pub fn collection_function() {
let mut v: Vec<i32> = Vec::new();//Vec类似ArrayList,称为向量?
v.push(12);//想要修改必须定义为mut可变的
println!("{:#?}", v);
let v = vec![1, 2, 3];
println!("{:#?}", v);
let v = vec![1, 2, 3, 4, 5];
let third = &v[2];//获取元素
println!("The third element is {}", third);
match v.get(2) {
Some(third) => println!("The third element is {}", third),
None => println!("There is no third element."),
}
//let does_not_exist = &v[100];//恐慌,编译时直接退出程序
///当将get方法传递给向量之外的索引时,它将返回None且不会出现惊慌。如果在正常情况下偶尔访问超出向量范围的元素,则可以使用此方法。
///您的代码将需要具有处理Some(&element)或None的逻辑,
let does_not_exist = v.get(100);
///当程序具有有效的引用时,借位检查器将执行所有权和借用规则
let mut v = vec![1, 2, 3, 4, 5];
let first = v[0];//使用引用&v[0]将会报错
v.push(6);//尝试保留第一个元素的引用的同时向向量添加元素将会出现编译错误,此时引用可能改变
println!("The first element is: {}", first);
///遍历向量集合
let v = vec![100, 32, 57];
for i in &v {
println!("{}", i);//直接打印 i,而不需要使用c语言中的*i,省略了*
}
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50;//对可变的向量进行操作,让每个元素都增加50
println!("{}", i)
}
///定义一个枚举以将不同类型的值存储在一个向量中
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
///Rust需要知道在编译时向量中将包含哪些类型,因此Rust确切知道要存储每个元素需要多少内存。第二个优点是,我们可以明确说明此向量允许哪些类型。
///如果Rust允许向量保留任何类型,则一个或多个类型可能会导致对向量元素执行的操作出错。使用枚举加上match表达式意味着Rust将确保在编译时处理所有可能的情况
///在编写程序时,如果您不知道该程序在运行时会存储到向量中的所有可能类型,则枚举技术将不起作用。相反,您可以使用trait对象
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
///使用字符串存储UTF-8编码文本
///Rust在核心语言中只有一种字符串类型,即字符串切片str,通常以借用的形式&str获取。
///字符串类型是Rust的标准库提供的,而不是编码为核心语言的字符串类型,它是一种可增长,可变,拥有的,以UTF-8编码的字符串类型。
///当rust开发者在Rust中引用“字符串”时,它们通常是指String和字符串切片&str类型,而不仅仅是这些类型之一。
///Rust的标准库还包括许多其他字符串类型,例如OsString,OsStr,CString和CStr。Library crates可以提供更多用于存储字符串数据的选项。
///看看这些名称如何全部以String或Str结尾?它们是指拥有和借用的变体,就像您之前看到的String和str类型一样。
///例如,这些字符串类型可以用不同的编码存储文本,或以不同的方式在内存中表示。
let data = "initial contents";
let s = data.to_string();
println!("{}", s);
let s = "initial contents".to_string();
println!("{}", s);
let s = String::from("initial contents");//从字符串文字(字符串常量 切片 &str类型)创建字符串(对象/String类型)
println!("{}", s);
//其他编码的文字
let hello = String::from("السلام عليكم");
let hello = String::from("Dobrý den");
let hello = String::from("Hello");
let hello = String::from("שָׁלוֹם");
let hello = String::from("नमस्ते");
let hello = String::from("こんにちは");
let hello = String::from("안녕하세요");
let hello = String::from("你好");
let hello = String::from("Olá");
let hello = String::from("Здравствуйте");
let hello = String::from("Hola");
///修改字符串,使用push_str方法将字符串切片附加到String
let mut s = String::from("foo");
s.push_str("bar");
let mut s1 = String::from("foo");
let s2 = "bar";
s1.push_str(s2);
println!("s2 is {}", s2);//s2被加到s1之后,再次使用s2
let mut s = String::from("lo");
s.push('l');//使用push向字符串值添加一个字符
///合并字符串
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; //s1被移动,之后无法再次使用
//+ 方法 使用add方法 fn add(self, s: &str) -> String {
//所以+组合字符串第二个参数必须是 &str的,但是这是因为编译器将&String转化为&str了。
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
///如果需要连接多个字符串,则+运算符的行为会变得笨拙:
let s = s1 + "-" + &s2 + "-" + &s3;
///对于更复杂的字符串组合,我们可以使用format! 宏
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{}-{}-{}", s1, s2, s3);
println!("{}", s);
///Rust字符串不支持索引
//let h = s1[0]; //编译报错
///与字符串内部的实现相关,字符串是Vec<u8>的包装
let len = String::from("Hola").len();
println!("{}", len);
let len = String::from("Здравствуйте").len();
println!("{}", len);
///当问到字符串有多长时,您可能会说12。但是,Rust的答案是24:这就是在UTF-8中编码“Здравствуйте”所需的字节数,因为该字符串中的每个Unicode标量值都占用2个字节的存储空间。
///因此,字符串字节的索引并不总是与有效的Unicode标量值相关。为了演示,请考虑以下无效的Rust代码:
let hello = "Здравствуйте";
//let answer = &hello[0];//编译报错
///Rust不允许我们索引到String中以获取字符的最后一个原因是索引操作总是需要恒定的时间(O(1))。
///但是用String不能保证性能,因为Rust必须从头到尾遍历所有内容以确定有多少个有效字符。
///有效的Unicode标量值可能由1个以上的字节组成,从rust字符串中获取字素簇很复杂,标准库并未提供此功能
for c in "नमस्ते".chars() {
println!("{}", c);//न म स ् त े ,一个字符由2个char组成
}
for b in "नमस्ते".bytes() {
println!("{}", b);//返回字节,很多个
}
///hash map
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
//从teams和initial_scores中创建map
let teams = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];
let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
///所有权
let field_name = String::from("Favorite color");
let field_value = String::from("Blue");
let mut map = HashMap::new();
map.insert(field_name, field_value);//此时field_name和field_value无效,已经被移动到map中
///获取key对应的value
let team_name = String::from("Blue");
let score = scores.get(&team_name);
///编译map
for (key, value) in &scores {
println!("{}: {}", key, value);
}
///更新map
//insert 覆盖旧值,scores.entry(String::from("Yellow")).or_insert(50);key不存在时插入
//根据旧值更新值
let text = "hello world wonderful world";
let mut map = HashMap::new();
for word in text.split_whitespace() {
let count = map.entry(word).or_insert(0);
*count += 1;
}
println!("{:?}", map);
}//v超出范围并在此处释放
///控制流
pub fn control_function() {
let number = 3;
//表达式结果必须是bool类型,不像c会自动将非bool转化为bool,if上不能使用文档注释
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
///处理多个if
let number = 6;//阴影,遮盖了前面的number
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
///与Scala一样,可以将if表达式的结果赋值给变量(这里的变量一般是指不可变的变量,虽然绕口,但是确实是事实)
let condition = true;
///从每个分支取得的if的返回值必须是同一类型,否则编译报错
let number = if condition {
5
} else {
6
};
println!("The value of number is: {}", number);
///循环
loop {
println!("again!");
break;//这个分号可省
}
///从循环中返回值
let mut counter = 0;
//循环赋值给变量
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
};//这个分号可省
};
///分号的使用还不清晰明确,后面再看
///暂时理解为,赋值给变量的代码块需要使用分号短句,不赋值可以不用分号,而表达式本身就是直接返回,使用分号反而不行。(return显示指定返回值)
println!("The result is {}", result);
///while循环
let mut number = 3;
///使用while可以减少大量的if else break
while number != 0 {
println!("{}!", number);
number -= 1;
};//这个分号可以省略
println!("LIFTOFF!!!");
///while变量数组
let a = [10, 20, 30, 40, 50];
let mut index = 0;
while index < 5 {
println!("the value is: {}", a[index]);
index += 1;
}
///使用for循环更加简单
///rust常用for,因为rust不会有迭代器失效的问题
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
println!("the value is: {}", element);
}
///使用倒数
for number in (1..4).rev() {
//输出3!2!1!LIFTOFF!!! print是没有换行的,与其他语言一致
print!("{}!", number);
}
println!("LIFTOFF!!!");
}
\ No newline at end of file
///枚举类型
pub fn enum_data_type() {
///定义枚举类型
enum IpAddrKind {
V4,
V6,
}
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
fn route(ip_kind: IpAddrKind) {}
///两个值IpAddrKind::V4和IpAddrKind::V6都具有相同的类型: IpAddrKind
route(IpAddrKind::V4);
route(IpAddrKind::V6);
struct IpAddr {
//在结构体中使用枚举
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
///数值直接放入每个枚举变量中,而不是需要使用结构体 struct IpAddr
enum IpAddr2 {
V4(String),
V6(String),
}
let home = IpAddr2::V4(String::from("127.0.0.1"));
let loopback = IpAddr2::V6(String::from("::1"));
///将枚举数值定义为不同类型,此时struct IpAddr无法实现
enum IpAddr3 {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr3::V4(127, 0, 0, 1);
let loopback = IpAddr3::V6(String::from("::1"));
///使用枚举
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
///使用结构体
struct QuitMessage; // 单位结构
struct MoveMessage {
x: i32,
y: i32,
}
struct WriteMessage(String); // 元祖结构
struct ChangeColorMessage(i32, i32, i32); // 元祖结构
///定义一个impl即可对所有枚举值生效,他们都能调用call
impl Message {
fn call(&self) {
// do something
}
}
let m = Message::Write(String::from("hello"));
m.call();
///Option类型
let some_number = Some(5);
let some_string = Some("a string");
//为None时需要指定类型,否则无法推断出类型
let absent_number: Option<i32> = None;
let x: i8 = 5;
let y: Option<i8> = Some(5);
//Option<i8> 与 i8 是不同的类型 <>是泛型,Option<T>表示任意的类型都可以放进Option
//let sum = x + y;
}
use std::cmp::Ordering;
use std::io;
use rand::Rng;
///简单的猜数字
pub fn example_guessing_game() {
println!("Guess the number!");
///thread_rng一个在当前执行线程本地且由操作系统播种的随机数生成器
let secret_number = rand::thread_rng().gen_range(1, 101);
//println!("The secret number is: {}", secret_number);
loop {
println!("Please input your guess.");
//变量默认是不可变的。使用mut表示变量是可变的,定义成let foo = 5; 则是不可变。
let mut guess = String::new();//关联函数,在类型上实现。一些语言称为静态方法。该函数创建了一个空串
//没有使用use,则这里需要写成 std::io::stdin
//&表示该参数是一个引用,Rust的主要优势之一是使用引用的安全性和便捷性
//&使您代码的多个部分可以访问同一条数据,而无需将该数据多次复制到内存中
io::stdin().read_line(&mut guess).expect("Failed to read line");
//无法比较数值与字符串需要转化为数值,Rust默认为i32
//Rust允许我们用新的值遮盖以前的值guess。此功能通常用于要将值从一种类型转换为另一种类型的情况。
//阴影使我们可以重用guess变量名,而不是强迫我们创建两个唯一变量,例如guess_str和guess。
//前面的guess是可变的,这个是不可变的。
//let guess: u32 = guess.trim().parse().expect("Please type a number!");//类型不明确,必须指定具体类型
//println!是宏
println!("You guessed: {}", guess);
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
//遇到无效输入直接跳过
Err(_) => continue,
};
println!("Please input your guess.");
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
//猜到正确数字后退出循环
break;
}
}
}
}
\ No newline at end of file
use std::fmt::{Debug, Display};
///通用类型,特征和寿命
pub fn largest_function() {
//在数字列表中查找最大数字的代码
let number_list = vec![34, 50, 25, 100, 65];
let mut largest = number_list[0];
for number in number_list {
if number > largest {
largest = number;
}
}
println!("The largest number is {}", largest);
///使用通用函数代码
let number_list = vec![34, 50, 25, 100, 65];
let result = largest1(&number_list);
println!("The largest number is {}", result);
let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];
let result = largest1(&number_list);
println!("The largest number is {}", result);
///使用更加通用的函数代码(泛型)
let number_list = vec![34, 50, 25, 100, 65];
let result = largest2(&number_list);
println!("The largest number is {}", result);
let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest2(&char_list);
println!("The largest char is {}", result);
}
///提取通用逻辑(此时类型都是i32,还可以进一步抽象为通用)
fn largest1(list: &[i32]) -> i32 {
let mut largest = list[0];
for &item in list.iter() {
if item > largest {
largest = item;
}
}
largest
}
///使用泛型并且限定泛型需要特质
fn largest2<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list.iter() {
if item > largest {
largest = item;
}
}
largest
}
///在结构中使用泛型
fn struct_generic() {
struct Point<T> {
x: T,
//由此可见,x/y它们具有相同的通用数据类型T
y: T,
}
let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };
struct Point2<T, U> {
x: T,
//x/y类型不同
y: U,
}
let both_integer = Point2 { x: 5, y: 10 };
let both_float = Point2 { x: 1.0, y: 4.0 };
let integer_and_float = Point2 { x: 5, y: 4.0 };
}
///在枚举和方法中定义
pub fn enum_generic() {
enum Option<T> {
Some(T),
None,
}
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
let p = Point { x: 5, y: 10 };
println!("p.x = {}", p.x());
///这意味着后面不能再实现任何其他类型impl
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
struct Point2<T, U> {
x: T,
y: U,
}
///多种泛型,方法自带了V W
impl<T, U> Point2<T, U> {
//V W仅与方法相关,T U是该结构的通用泛型
fn mixup<V, W>(self, other: Point2<V, W>) -> Point2<T, W> {
Point2 {
x: self.x,
y: other.y,
}
}
}
let p1 = Point2 { x: 5, y: 10.4 };
let p2 = Point2 { x: "Hello", y: 'c' };
let p3 = p1.mixup(p2);
println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
//Rust通过在编译时对使用泛型的代码进行单态化来实现这一点。单色化是通过填充编译时使用的具体类型,将通用代码转换为特定代码的过程。
//所以rust泛型高效
}
///特性:定义共同的行为
pub fn trait_function() {
///定义一个特质
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
///在NewsArticle类型上,实现特质Summary
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
///在Tweet类型上,实现特质Summary
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
///使用特质Summary的summarize方法
let tweet = Tweet {
username: String::from("horse_ebooks"),
content: String::from("of course, as you probably already know, people"),
reply: false,
retweet: false,
};
println!("1 new tweet: {}", tweet.summarize());
//只有特征或类型在板条箱中是本地的时,我们才能对类型实现特征
//此规则可确保其他人的代码不会破坏您的代码,反之亦然。
//孤立规则(滑稽,翻译的):如果没有该规则,则两个包装箱可能会针对同一类型实现相同的特征,而Rust不会知道要使用哪种实现
///使用特质的默认使用:impl Summary for NewsArticle {}。
///使用特质作为参数
pub fn notify(item: impl Summary) {
println!("Breaking news! {}", item.summarize());
}
///特质绑定语法,与上面的效果类似但不等价
pub fn notify2<T: Summary>(item: T) {
println!("Breaking news! {}", item.summarize());
}
///使用impl Summary作为参数会更加方便,此时只要求item1和item2参数实现了Summary,而不要求他们类型完全一致
pub fn notify3(item1: impl Summary, item2: impl Summary) {
println!("Breaking news! {}", item1.summarize());
}
///需要强制让item1和item2的类型是一致的,则必须要使用特质绑定
pub fn notify4<T: Summary>(item1: T, item2: T) {
println!("Breaking news! {}", item1.summarize());
}
///使用+,限制参数必须同时实现多个特质
pub fn notify5(item: impl Summary + Display) {
println!("Breaking news! {}", item.summarize());
}
///使用特质绑定,限定T必须实现了两个特质
pub fn notify6<T: Summary + Display>(item: T) {
println!("Breaking news! {}", item.summarize());
}
///这样写太麻烦
fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 { 1 }
//简化
fn some_function2<T, U>(t: T, u: U) -> i32 where T: Display + Clone, U: Clone + Debug { 1 }
///返回实现特征的类型
fn returns_summarizable() -> impl Summary {
Tweet {
username: String::from("horse_ebooks"),
content: String::from("of course, as you probably already know, people"),
reply: false,
retweet: false,
}
}
//下面是无效的,编译不过
//由于在编译器中实现impl Trait语法方面的限制,因此不允许返回NewsArticle或Tweet
// fn returns_summarizable2(switch: bool) -> impl Summary {
// if switch {
// NewsArticle {
// headline: String::from("Penguins win the Stanley Cup Championship!"),
// location: String::from("Pittsburgh, PA, USA"),
// author: String::from("Iceburgh"),
// content: String::from("The Pittsburgh Penguins once again are the best
// hockey team in the NHL."),
// }
// } else {
// Tweet {
// username: String::from("horse_ebooks"),
// content: String::from("of course, as you probably already know, people"),
// reply: false,
// retweet: false,
// }
// }
// }
struct Pair<T> {
x: T,
y: T,
}
impl<T> Pair<T> {
fn new(x: T, y: T) -> Self {
Self {
x,
y,
}
}
}
///始终实现new函数,但是Pair<T>仅在内部类型T实现了实现比较的PartialOrd特质和实现打印的Display特质的情况下,才实现cmp_display方法。
///根据特质范围有条件地在泛型类型上实现方法
impl<T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.x >= self.y {
println!("The largest member is x = {}", self.x);
} else {
println!("The largest member is y = {}", self.y);
}
}
}
}
///生命周期
fn lifetimes_function() {
//使用生命周期验证引用
//在大多数情况下,生存期是隐式和推断的,就像在大多数情况下一样,推断类型。
//当可能有多个类型时,必须注释类型。以类似的方式,当引用的生存期可以通过几种不同方式关联时,我们必须注释生存期。
//Rust要求我们使用通用生命周期参数注释关系,以确保在运行时使用的实际引用绝对有效。
//生命周期的主要目的是防止引用悬而未决,从而导致程序引用的数据不是其要引用的数据。
// {
// r和x的生命周期注释,分别命名为'a和'b
// let r; // ---------+-- 'a
// |
// { // |
// let x = 5; // -+-- 'b |
// r = &x; // | |
// } // -+ |
// |
// println!("r: {}", r); // |
// } // ----------+
///正确代码
{
let x = 5; // ----------+-- 'b
// |
let r = &x; // --+-- 'a |
// | |
println!("r: {}", r); // | |
// --+ |
} // ----------+
///函数的通用生命周期
///过于复杂,建议有时间再研究:https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html
// &i32 // 一个引用
// &'a i32 // 具有明确生命周期的引用
// &'a mut i32 // 具有显式寿命的可变引用
///指定签名中的所有引用必须具有相同的生存期 'a
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
}
......@@ -75,7 +75,7 @@ mod back_of_house {
//mod front_of_house;//在mod front_of_house之后使用分号(而不是使用块)会告诉Rust从另一个与模块同名的文件中加载模块的内容。
//重新导出,使名称可用于新范围内的任何代码 (因为我们将一个项目放入范围内,同时也使该项目可供其他人进入其范围)
//pub use 重新导出,使名称可用于新范围内的任何代码 (因为我们将一个项目放入范围内,同时也使该项目可供其他人进入其范围)
//use std::collections::*; //导入所有内部的类型
pub fn eat_at_restaurant2() {
......
此差异已折叠。
///简洁的控制流语法
pub fn match_syntax2() {
let some_u8_value = Some(0u8);
match some_u8_value {
Some(3) => println!("three"),
_ => () //这行是多余的样板代码
}
//使用if let 省略上面的样板代码
if let Some(3) = some_u8_value {
println!("three");
}
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
//使用if let简化代码
fn value_in_cents(coin: Coin) -> u8 {
let mut count = 0;
if let Coin::Quarter(state) = coin {
println!("State quarter from {:?}!", state);
} else {
count += 1;
}
1
}
}
pub fn match_syntax() {
///枚举与match
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
fn value_in_cents2(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
///代码多时需要使用花括号,并且最后一行返回值不加分号。大括号后面仍是逗号
println!("Lucky penny!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
///绑定到值的匹配
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
}
enum Coin2 {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn value_in_cents3(coin: Coin2) -> u8 {
match coin {
Coin2::Penny => 1,
Coin2::Nickel => 5,
Coin2::Dime => 10,
Coin2::Quarter(state) => {
println!("State quarter from {:?}!", state);
25
}
}
}
///Option类型match
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
//这行代码多余,但是又不能省略,否则编译报错。
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
///使用占位符,编译通过
let some_u8_value = 0u8;
match some_u8_value {
1 => println!("one"),
3 => println!("three"),
5 => println!("five"),
7 => println!("seven"),
_ => ()
}
}
///方法语法
pub fn method_syntax() {
///方法语法,方法与函数不同
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
///结构体的实现块,方法第一个参数必须是&self,不需要声明类型(impl与struct名称相同,能自动推断self类型,这也是能自动引用、取消引用的原因)
impl Rectangle {
//把2个方法放在多个impl实现也是可以的
fn area(&self) -> u32 {
self.width * self.height
}
//新增方法,有额外参数
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
//关联函数,没有self,类似其他语言的静态方法,但不是rust方法
fn square(size: u32) -> Rectangle {
Rectangle { width: size, height: size }
}
}
let rect1 = Rectangle { width: 30, height: 50 };
///c/c++中如果object是一个指针, object->something() 与 (*object).something()等价
///Rust没有等效于->运算符;相反,Rust具有称为自动引用和取消引用的功能。调用方法是Rust少数具有这种行为的地方之一。
///工作原理如下:当您使用object.something()调用方法时,Rust会自动添加&,&mut或*,从而使对象与方法的签名匹配。换句话说,以下是相同的:
///p1.distance(&p2);
///(&p1).distance(&p2);
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
///方法参数
let rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { width: 10, height: 40 };
let rect3 = Rectangle { width: 60, height: 45 };
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
///impl块的另一个有用功能是允许我们在不以self为参数的impl块中定义函数。这些之所以称为关联函数,是因为它们与struct相关联。
///它们仍然是函数,而不是方法,因为它们没有可使用的结构实例
///关联函数通常用于将返回该结构的新实例的构造函数。例如,我们可以提供一个关联的函数,该函数将具有一个维度参数并将其用作宽度和高度,从而使创建矩形矩形变得更加容易,而不必两次指定相同的值:
let sq = Rectangle::square(3);//类似调用静态方法
println!("sq is {:#?}", sq);
///关联函数与结构体相关,但是没有self实例,他们仍是函数!!!
struct Test;
impl Test {
fn test() -> String {
String::from("hello world")
}
}
let test = Test::test();
println!("test is {:#?}", test);
}
\ No newline at end of file
use rust_exercise::front_of_house;
//引用外部的条板箱
pub fn crate_function() {
front_of_house::add_to_waitlist()
}
//pub mod lib;
//
//fn crate_function_lib() {
// lib::eat_at_restaurant();
// lib::eat_at_restaurant2();
//}
pub fn return_function() {
let s1 = gives_ownership(); // lets_ownership移动其返回值到s1中
let s2 = String::from("hello"); // s2进入范围
let s3 = takes_and_gives_back(s2); // s2被移入takes_and_gives_back, takes_and_gives_back的返回值被移动到s3
println!("{},{}", s1, s3);
fn gives_ownership() -> String { // gives_ownership会其返回值移动到调用它的函数中
let some_string = String::from("hello"); // some_string进入范围
some_string // 返回some_string字符串并移到调用函数
}
// take_and_gives_back将获取一个String并返回一个
fn takes_and_gives_back(a_string: String) -> String { // a_string进入范围
a_string // 返回a_string并移至调用函数
}
}
pub fn fib(n: i32) -> i32 {
if n == 0 {
0
} else if n == 1 {
1
} else {
fib(n - 1) + fib(n - 2)
}
}
pub fn fib_2(n: i32) -> i32 {
let mut a = 0;
let mut b = 1;
let mut c = 0;
if n == 0 || n == 1 {
n
} else {
for number in 2..(n + 1) {
c = a + b;
a = b;
b = c;
}
c
}
}
//具有返回值的rust函数
pub fn five() -> i32 {
///这里同样,由于需要返回值为i32类型,增加了分号表示语句,没有返回值(实际是空括号),所以导致类型不一致,编译会报错
5
}
pub fn another_function(x: i32) {
//传参数的rust函数,与Scala一样,名称: 类型
println!("The value of x is: {}", x);
}
\ No newline at end of file
use std::{fs, io};
use std::error::Error;
use std::fs::File;
use std::io::{ErrorKind, Read};
///恐慌使用(error)
pub fn panic_function() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating the file: {:?}", e),//panic!打印错误信息,panic是不可恢复的错误
},
other_error => panic!("Problem opening the file: {:?}", other_error),
},
};
///使用闭包简化代码
//展开一个结果,得到一个[`Ok`]的内容。如果值是一个[`Err`],则用它的值调用'op'。
let f = File::open("hello.txt").unwrap_or_else(|error| {
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Problem creating the file: {:?}", error);
})
} else {
panic!("Problem opening the file: {:?}", error);
}
});
///如果Result值为Ok变体,unwrap则将在中返回该值Ok。如果Result是Err变体,unwrap将为panic!我们调用宏。
let f = File::open("hello.txt").unwrap();
//一样,但是是自己传递错误信息给panic!宏
let f = File::open("hello.txt").expect("Failed to open hello.txt");
///将错误返回到调用代码的函数
fn read_username_from_file() -> Result<String, io::Error> {
let f = File::open("hello.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}
///传播错误的捷径:?运算符
fn read_username_from_file2() -> Result<String, io::Error> {
let mut f = File::open("hello.txt")?;//使用?运算符将错误返回到调用代码的函数,将错误自身转换为返回的错误类型。(From特质 from函数)
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
fn read_username_from_file3() -> Result<String, io::Error> {
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
}
fn read_username_from_file4() -> Result<String, io::Error> {
fs::read_to_string("hello.txt")
}
//下面是在main方法中使用?的示例
// fn main() -> Result<(), Box<dyn Error>> {
// let f = File::open("hello.txt")?;
//
// Ok(())
// }
///创建一个新类型并将验证放入函数中以创建该类型的实例
//这样做的目的是,其他代码中不需要混入对该类型的检查
pub struct Guess {
value: i32,
}
impl Guess {
pub fn new(value: i32) -> Guess {
if value < 1 || value > 100 {
panic!("Guess value must be between 1 and 100, got {}.", value);
}
Guess {
value
}
}
pub fn value(&self) -> i32 {
self.value
}
}
}
\ No newline at end of file
///指针(引用)
pub fn empty_point_function() {
//let reference_to_nothing = dangle();
let reference_to_nothing = no_dangle();
fn no_dangle() -> String {
String::from("hello")// 直接反回函数的值,不能加分号
}
//编译报错,因为s是在dangle内部创建的,所以当dangle的代码完成时,将释放s。但是我们试图返回对它的引用。这意味着该引用将指向无效的String。Rust不允许我们这样做。
// fn dangle() -> &String {
// let s = String::from("hello");
// &s
// }
}
pub fn point_function() {
fn calculate_length(s: &String) -> usize {
s.len()
}
let s1 = String::from("hello");
///类似c/c++传递指针/引用
let len = calculate_length(&s1);
///s1在之后还能使用
println!("The length of '{}' is {}.", s1, len);
}
pub fn copy_function() {
let x = 5;
///基本类型在移动时使用copy,x不会失效。
let y = x;
println!("x = {}, y = {}", x, y);
///使用clone克隆数据,目前先理解为深拷贝
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
}
pub fn tuple_function() {
let s1 = String::from("hello");
let (s2, len) = calculate_length(s1);
println!("The length of '{}' is {}.", s2, len);
fn calculate_length(s: String) -> (String, usize) {
let length = s.len(); // len() 返回字符串的长度
(s, length)
}
}
///简单数据类型与数组,所有权
pub fn simple_array_data_type() {
///--release模式下,整数溢出将会变为最小值
///在u8(0-255)类型下,256变为0,257变为1,依此类推
///默认浮点类型是f64,相当于Java double,IEEE754标准
let x = 2.0; // f64
let y: f32 = 3.0; // f32
///数值运算,与其他语言相同,类型可以自动推断,不用指定类型
// addition
let sum = 5 + 10;
// subtraction
let difference = 95.5 - 4.3;
// multiplication
let product = 4 * 30;
// division
let quotient = 56.7 / 32.2;
// remainder
let remainder = 43 % 5;
let t = true;
///显示指定类型
let f: bool = false;
///字符类型,Unicode,4bytes
let c = 'z';
let z = 'ℤ';
let heart_eyed_cat = '😻';
///元组类型,与Scala基本相同,可以推断出类型
let tup: (i32, f64, u8) = (500, 6.4, 1);
let tup = (500, 6.4, 1);
///提取出元组的每个值
let (x, y, z) = tup;
println!("The value of y is: {}", y);
///使用 .获取元组的值,从0下标开始
let five_hundred = tup.0;
let six_point_four = tup.1;
let one = tup.2;
///数组类型,一般在只有固定元素个数的时候使用
let array = [1, 2, 3, 4, 5];
///初始化数组的第二种方法
let a: [i32; 5] = [1, 2, 3, 4, 5];
///等价于let a = [3, 3, 3, 3, 3];,意为5个3构成的数组
let a = [3; 5];
///访问数组,同样是从0下标开始
let first = a[0];
let second = a[1];
///Rust通过立即退出而不是允许内存访问并继续操作来保护您免受此类错误的侵害
let element = a[0];//若下标大于数组索引则运行时检查并报错退出"error: index out of bounds: the len is 5 but the index is 10"
}
///rust String比较复杂
pub fn string_function() {
let mut s = String::from("hello");
s.push_str(", world!"); // push_str() 将文字附加到字符串
println!("{}", s); //打印 hello, world!
let s = String::from("hello"); // s进入范围
takes_ownership(s); // s的值移动到函数,所以在这里不再有效
//println!("{}", s);//编译错误:value borrowed here after move。出借后的s被移动,后续不可用
let x = 5; // x进入范围
makes_copy(x); // x将移动到函数
//但是i32是Copy,所以之后还可以使用
println!("{}", x);//正常打印
fn takes_ownership(some_string: String) {
println!("{}", some_string);
} //在这里,some_string超出范围并调用`drop`。内存释放
fn makes_copy(some_integer: i32) {
println!("{}", some_integer);
}
}
\ No newline at end of file
///结构体
pub fn struct_data_type() {
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
let mut user1 = User { //必须定义为可变的才能修改结构体的内容
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
user1.email = String::from("anotheremail@example.com");
//对应参数名和结构体属性名称相同的,可以省略以减少代码
fn build_user(email: String, username: String) -> User {
User {
email: email,
username: username,
active: true,
sign_in_count: 1,
}
}
//更好的方法是省略参数名,结构体没有顺序要求,与元祖不同,元祖结构体:仅声明类型的结构体
fn build_user_better(email: String, username: String) -> User {
User {
email,
username,
active: true,
sign_in_count: 1,
}
}
//创建一个新的结构体user2,并使用user1的某些值
let user2 = User {
email: String::from("another@example.com"),
username: String::from("anotherusername567"),
active: user1.active,
sign_in_count: user1.sign_in_count,
};
//更好的方式是使用 ..语法,其余字段应与给定实例(user1)中的字段具有相同的值
let user2 = User {
email: String::from("another@example.com"),
username: String::from("anotherusername567"),
..user1
};
//元祖结构体,没有命名属性字段,仅有类型声明
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
//此结构体存储切片。而上面的结构体存储的是String类型,下面代码编译会报错,因为使用切片时需要指定生命周期。与使用String拥有所有权不同,&str没有获取所有权而只是借用
// struct User2 {
// username: &str,
// email: &str,
// sign_in_count: u64,
// active: bool,
// }
let width1 = 30;
let height1 = 50;
fn area(width: u32, height: u32) -> u32 {
width * height
}
println!("The area of the rectangle is {} square pixels.", area(width1, height1));
//使用元祖
fn area_tuple(dimensions: (u32, u32)) -> u32 {
dimensions.0 * dimensions.1
}
let rect1 = (30, 50);
println!(
"The area of the rectangle is {} square pixels.",
area_tuple(rect1)
);
//使用结构体赋予更多含义
#[derive(Debug)] //使得该结构体能在println!中被打印
struct Rectangle {
width: u32,
height: u32,
}
fn area_struct(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The area of the rectangle is {} square pixels.",
area_struct(&rect1)
);
//这将会报错,因为该结构体不支持显示:`Rectangle` doesn't implement `std::fmt::Display`
println!("rect1 is {:#?}", rect1);//{:?}使用调试模式打印也会报错:`Rectangle` doesn't implement `std::fmt::Debug`,{:#?} 格式化打印
}
\ No newline at end of file
///变量 表达式
pub fn variables_function() {
//默认i32,带符号32位整数
let x = 5;
println!("The value of x is: {}", x);
// x = 6; 不可变的,编译不过
// println!("The value of x is: {}", x);
let mut y = 6;
println!("The value of y is: {}", y);
y = 7;//可变的变量
println!("The value of y is: {}", y);
//常量,必须指定类型,不可省略
const MAX_POINTS: u32 = 100_000;
println!("The value of const value is: {}", MAX_POINTS);
///阴影允许定义变量与前面重名,前者被遮蔽
///mut与shadowing区别:后者将创建一个新的变量,因此可以改变类型,使用相同的名称,常见用法如下:
//let spaces = " ";
//let spaces = spaces.len();//使用相同名称但类型已经发生变化
///但是对于mut则不能,spaces虽然是可变的,但是类型是字符串类型的
//let mut spaces = " ";
//spaces = spaces.len();
let i = 5;
let i = x + 1;
let i = x * 2;
println!("The value of x is: {}", i);
}
pub fn try_change_function() {
///必须都是mut的,否则编译就会报错,不可变,无法被改变
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
let mut s = String::from("hello");
change(&mut s);
let mut s = String::from("hello");
let r1 = &mut s;
//let r2 = &mut s;//可变引用只能被出借一次,这里将会编译报错
//println!("{}, {}", r1, r2);
let mut s = String::from("hello");
{
let r1 = &mut s;
} // r1在这里超出范围,因此我们可以毫无问题地进行新引用。
let r2 = &mut s;//正常使用,虽然上面已经用过s
let mut s = String::from("hello");
let r1 = &s; // 没问题,与上面两次mut出借不一样,这里是没有mut,所以对于不可变引用,可以使用多次次,且不可在拥有不可变引用时同时拥有可变引用
let r2 = &s; // 没问题
//let r3 = &mut s; // 有问题,不可变在后面却是可变,不允许,编译报错
//println!("{}, {}, and {}", r1, r2, r3);
let mut s = String::from("hello");
let r1 = &s; // 没问题
let r2 = &s; // 没问题
println!("{} and {}", r1, r2);
//在此之后不再能使用r1和r2
let r3 = &mut s; // 没问题,因为r1 r2进入println! 并且在此之后会失效,与所有权有关。
println!("{}", r3);
}
pub fn expr_function() {
//赋值需要返回值,rust语句没有返回值,不同于其他语言赋值可以连用
// let x = (let y = 6);
let x = 5;
let y = {
let x = 3;
x + 1 //返回x+1,且不能用分号,有分号表示这个是语句,没有返回值,无法赋值给y
};
println!("The value of y is: {}", y);
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册