const A_CONSTANT_VALUE: i32 = 6; let tmp: i32 = 5; // "tmp" attempt to use a non-constant value in a constant const YET_ANTHOER_CONSTANT_VALUE: i32 = A_CONSTANT_VALUE * tmp;
let x = 5; let x = 6; // 允许的语法,x为6
// u32不加的话,会报出compile error, 因为rust无法推断guess的类型 let guess: u32 = "42".parse().expect("Not a number!");
Length | Signed | Unsigned |
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
128-bit | i128 | u128 |
arch | isize | usize |
fn main1() { let tup = (500, 6.4, 1); let (x, y, z) = tup; println!("The value of y is: {y}"); } fn main2() { let x: (i32, f64, u8) = (500, 6.4, 1); let five_hundred = x.0; let six_point_four = x.1; let one = x.2; }
let a: [i32; 5] = [1, 2, 3, 4, 5];
x + 1
和x + 1;
的区别,一个是expression, 一个是statement。fn main() { // {..}代表一个block let y = { let x = 3; x + 1 // 没有分号,代表一个expression,即当前block返回4,同时赋值给y }; println!("The value of y is: {y}"); }
fn five() -> i32 { // 使用->定义返回类型。 5 }
fn main() { let number = 6; 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"); } // 可以直接赋值 let number = if true { 5 } else { 6 }; println!("The number is {}", number); // infinite loop // loop { // println!("again"); // } // 使用loop + break直接使用循环对一个变量赋值 let mut counter = 0; let result = loop { counter += 1; if counter == 10 { break counter * 2; } }; println!("Result is {result}"); // while loop let mut count: i32 = 5; while count != 1 { println!("in while loop"); count -= 1; } // for loop an array let a = [10, 20, 30, 40, 50]; for element in a { println!("the value is: {element}"); } }
s2 = s1
后,s1的owner会转移到s2,也就是说执行完成后s2为这块内存的owner,s1直接被gc。所以在这行代码执行完成后s1不再存在。但是可以clone(类似deep clone)来复制这片内存空间let s1 = String::from("hello"); // s1已经move到了s2, s1在后续scope中不再存在 // let s2 = s1; // 可以使用clone来进行shallowcopy let s2 = s1.clone(); println!("{}, world!", s1);
Copy
的Trait到对应类型中,带有Copy Trait的类型可以保证值在assign后继续时候(被copy)。相对应的,在内存中的数据类型(例如String)会带有一个叫做Drop
的Trait,实现Drop
的Trait不能实现Copy
。Copy
trait的类型一共5种:即4个初始类型(scalar types)还有仅含有初始类型的tuple,例如(i32, i32)
,而(i32, String)
则不是,因为String不是初始类型fn main() { let s1 = String::from("hello"); let (s2, len) = calculate_length(s1); // 由于s1的ownership转换,s1不再是s2,s1无法被重用 println!("The length of '{}' is {}.", s2, len); } fn calculate_length(s: String) -> (String, usize) { let length = s.len(); // len() returns the length of a String (s, length) }
&
符号代表着取对应指针的引用。fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); println!("The length of '{}' is {}. The Ref is {}", s1, len, &s1); } fn calculate_length(s: &String) -> usize { s.len() }
s1
s
的关系可以用下图说明, 即s
就是一个新的指针指向s1
&s1
在传入函数后,可以继续使用的原因是&s1
是s1
的一个引用,但是因为他不是对应值的owner,所以不会drop。且函数不需要返回来交出ownership,因为引用压根不存在ownership一说。&mut
来创建一个可修改的引用。fn main() { let mut s = String::from("hello"); change(&mut s); // The changed s is hello world println!("The changed s is {s}"); } fn change(some_string: &mut String) { some_string.push_str(", world"); }
fn main() { let mut s = String::from("hello"); change(&mut s); println!("The changed s is {s}"); { let r1 = &mut s; r1.push_str(" inner") } // r1 goes out of scope here, so we can make a new reference with no problems. let r2 = &mut s; r2.push_str(" outter"); // after two &mut, s is $hello, world inner outter println!("after two &mut, s is ${s}") } fn change(some_string: &mut String) { some_string.push_str(", world"); }
let mut s = String::from("hello"); let r1 = &s; // no problem let r2 = &s; // no problem let r3 = &mut s; // BIG PROBLEM println!("{}, {}, and {}", r1, r2, r3);
let mut s = String::from("hello"); let r1 = &s; // no problem let r2 = &s; // no problem println!("{}, {}", r1, r2); let r3 = &mut s; r3.push_str(" world"); // r1, r2的scope在这结束,而在这中间,r3这个mut reference的scope相互重叠了。 println!("{}, {}, {}", r1, r2, r3); // println!("{}", r3); // 使用这行将解决这个问题
fn main() { let reference_to_nothing = dangle(); } fn dangle() -> &String { let s = String::from("hello"); // s is dropped after dangle fn(), but we try to return a reference value to it. &s }
mut
..
符号,merge的顺序与js相反。且..
的merge必须放在最后一行。struct User { active: bool, username: String, email: String, sign_in_count: u64, } fn build_user(email: String, username: String) -> User { User { active: true, username: username, email: email, sign_in_count: 1, } } fn main() { let mut user1 = User { active: true, username: String::from("someusername123"), email: String::from("someone@example.com"), sign_in_count: 1, }; // user1需要整体标注为mut user1.email = String::from("anotheremail@example.com"); let user2 = build_user( String::from("citrus327@outlook.com"), String::from("hophop") ); // 取user1的username,剩下的采用user2,而非使用user2进行全量覆盖 let merged_user = User {username: user1.username, sign_in_count: user2.sign_in_count, ..user2 }; println!("user email is {}", user1.email); // user1.username已经被merged_user借用,无法再次使用。 // println!("user email is {}", user1.username); // 而sign_in_count的类型属于u64, 属于scalar type, 可以再次使用(实现了Copy Trait) println!("user2 sign_in_count is {}", user2.sign_in_count); println!("merged user name is {}", merged_user.username); // someusername123 println!("merged user email is {}", merged_user.email); // citrus327@outlook.com }
Rectangle::print()
来调用。apply
#[derive(Debug)] struct Rectangle { width: u32, height: u32 } impl Rectangle { fn area(&self) -> u32 { self.width * self.height } // method fn print(&self) { println!("The area of the rectangle {:#?} is {}", self, self.area()); } // associated functions fn square(size: u32) -> Self { Self { width: size, height: size, } } } fn main() { let rectangle = Rectangle { width: 40, height: 40 }; // println!("The area of the rectangle {:#?} is {}", rectangle, calc_area(&rectangle)); // println!("The area of the rectangle {:#?} is {}", rectangle, rectangle.area()); rectangle.print(); // 可以使用这种方式(associated functions)调用。 Rectangle::print(&rectangle); // associated functions的调用方式 let square = Rectangle::square(12); square.print(); } fn calc_area (rectangle: &Rectangle) -> u32{ return rectangle.width * rectangle.height; }
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), }
Message
枚举拥有4种值,且每种值可以hold不同类型的数据。Option
,Option
枚举拥有2个值,None
和Some
,None
代表不存在,Some
代表值存在。None
和Some
可直接在代码中引用。或使用通用的枚举synxtax,例如Option::None
the concept that null is trying to express is still a useful one: a null is a value that is currently invalid or absent for some reason.
let x: i8 = 5; let y: Option<i8> = Some(5); let sum = x + y;
y
可能为null
时,使用Some
来代表y
这个值能不存在,当y
被引用时,一个i8
是无法与一个Option<i8>
类型相加的,所以需要一定的方式将Some<T>
转换为T
,进而进行使用。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, } }
other
关键字或者_
关键字进行额外分支的处理。enum Coin { Penny, Nickel, Dime, Quarter(UsState), } fn value_in_cents(coin: Coin) -> u8 { match coin { Coin::Penny => 1, // Coin::Nickel => 5, // Coin::Dime => 10, Coin::Quarter(state) => { println!("the state is from {:?}", state); return 25; } // 使用other做缺省处理,例如js中switch case的default other => { println!("other"); return 2; } } } fn main() { println!("value is {}", value_in_cents(Coin::Nickel)); }
_
符号指不需要对额外分支做处理。if let
简化pattern matching的冗余代码let config_max = Some(3); match config_max { Some(max) => println!("The maximum is configured to be {}", max), _ => (), }
config_max
是一个为3的Some
,当Some
通过match
检测后,打印出max
的值,否则不做任何事情。在这种case下_
的分支就会冗余。if let
可以简化以上代码为:let config_max = Some(3u8); if let Some(max) = config_max { println!("The maximum is configured to be {}", max); }
=
左侧的为需要match的条件,=
右侧为被match的变量,对应的大括号内的为首个映射上的分支处理代码。unwrap_or
unwrap()
Option
or Result
type, assuming that it contains a value. If the Option
or Result
is Some(value)
or Ok(value)
, unwrap()
returns the inner value. However, if the Option
or Result
is None
or Err
, unwrap()
will panic and crash the program.unwrap_err
Result
types. It is the counterpart of unwrap()
for handling Err
variants. If the Result
is Ok(value)
, unwrap_err()
will panic and crash the program. But if the Result
is Err(error)
, it returns the inner error value.fn divide(a: i32, b: i32) -> Result<i32, String> { if b == 0 { Err(String::from("Cannot divide by zero")) } else { Ok(a / b) } } fn main() { let result = divide(10, 0); let err_message = result.unwrap_err(); println!("Error occurred: {}", err_message); }
unwrap_or(default)
Option
or Result
, or provide a default value if it is None
or Err
. If the Option
or Result
is Some(value)
or Ok(value)
, unwrap_or()
returns the inner value. But if the Option
or Result
is None
or Err
, it returns the provided default
value.unwrap_or_default()
unwrap_or()
, but it returns the default value of the inner type if the Option
or Result
is None
or Err
. The default value is determined by the Default
trait implemented for the inner type.unwrap_or_else(default_fn)
Option
or Result
, or provide a fallback value or mechanism if it is None
or Err
. If the Option
or Result
is Some(value)
or Ok(value)
, unwrap_or_else()
returns the inner value. But if the Option
or Result
is None
or Err
, it calls the provided default_fn
closure or function to generate the fallback value.fn divide(a: i32, b: i32) -> Result<i32, String> { if b == 0 { Err(String::from("Cannot divide by zero")) } else { Ok(a / b) } } fn main() { let result = divide(10, 2); let value = result.unwrap_or_else(|err| { println!("Error occurred: {}", err); 0 }); println!("Result: {}", value); }
// 从0创建 let mut v: Vec<i32> = Vec::new(); // mutating vector v.push(1); v.push(2); v.push(3); v.push(4); // 包含初始值 let v = vec![1, 2, 3, 4, 5];
push
, pop
等, 不过多赘述fn iterate_vec(v: &Vec<i32>) { for i in v { println!("the value at index {i} is {}", i); } } fn reading_vec_by_index(v: &Vec<i32>, index: usize) { println!( "reading index {} from vector {:?}, value is {}", index, v, v[index] ); } fn reading_vec_by_get(v: &Vec<i32>, index: usize) { // let third3 = v.get(index); // let result: i32; // match third3 { // Some(v) => result = *v, // None => println!("There is no third element."), // } // return result; // 使用.get去获取值,将会返回一个Option let third4 = v.get(index); if let Some(third) = third4 { println!( "reading index {} from vector {:?}, value is {}", index, v, third ); } else { println!("There is no third element."); } }
fn main() { println!("Hello, world!"); let str = create_str_from_new(); print_str(&str); let str = create_str_with_initial_values(); print_str(&str); let mut str = create_str_from_string_literal(); print_str(&str); str.push_str(" world"); // can push str str.push('!'); // can only push char print_str(&str); // use + operator to append let str_part_1 = String::from("hello"); let str_part_2 = String::from("world"); // 需要使用&进行引用的原因是因为 + operator本质是调用add function,add fn的定义要求传入&str // add fn: fn add(self, s: &str) -> String { // 因为str_part_1的ownership被add拿走,str_part_1在+号后不再存在,而str_part_2为引用,后续依旧可用。 let str = str_part_1 + " " + &str_part_2 + "!"; // print_str(&str_part_1); // 报错 ownership // print_str(&str_part_2); // OK print_str(&str); let s1 = String::from("tic"); let s2 = String::from("tac"); let s3 = String::from("toe"); let str = s1 + "-" + &s2 + "-" + &s3; print_str(&str); let s1 = String::from("tic"); let s2 = String::from("tac"); let s3 = String::from("toe"); let str = format!("{s1}-{s2}-{s3}"); print_str(&str); // string不支持index访问。 // 原因是String的本质实现是存放着asciicode的vector: Vec<u8>, // 若允许indexing,则会返回相对应的number,为了保证意外的情况,Rust完全禁止了String indexing // let s1 = String::from("hello"); // let h = s1[0]; // Compile Error } fn create_str_from_new() -> String { let mut s = String::new(); s.push('h'); s.push('e'); s.push('l'); s.push('l'); s.push('o'); s } fn create_str_with_initial_values() -> String { let s = String::from("hello"); s } fn create_str_from_string_literal() -> String { let s = "hello"; s.to_string() } fn print_str(str: &str) { println!("The str created is {}", str); }
Vec<u8>
实现,通过index访问将会返回对应的ascii codeनमस्ते
,从bytes角度看,展示为[224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164,
224, 165, 135]
,若能够通过index访问,将会返回number,且无法明确对应的index值。O(1)
复杂度,但实际并不是如此,且无法保证let str = "Здравствуйте"; let s = &str[0..4]; // Зд本身占用4个bytes,0-4会得到Зд,而不是Здра print_str(&s);
Vec<u8>
这个设计的问题。。能不用别用了。。for c in "Зд".chars() { println!("{c}"); }
use std::collections::HashMap; fn main() { let scores = create_hash_map(); let score = scores.get("Blue").copied().unwrap_or(0); println!("score is {}", score); iterate_hash_map(&scores); let field_name = String::from("Favorite color"); let field_value = String::from("Blue"); let mut map = HashMap::new(); map.insert(field_name, field_value); // map.insert(&field_name, &field_value); // ownership已经被转移到了map,后续无法再获取field_name和field_value // println!("field_name {}", field_name); // println!("field_value {}", field_value); update_hash_map(); } fn create_hash_map() -> HashMap<String, i32> { let mut scores = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Yellow"), 50); scores } fn iterate_hash_map(map: &HashMap<String, i32>) { for (key, value) in map { println!("{key}: {value}"); } } fn update_hash_map() { let mut scores = HashMap::new(); // overwriting scores.insert(String::from("Blue"), 10); scores.insert(String::from("Blue"), 25); // overwriting by checking if already has value scores.entry(String::from("Yellow")).or_insert(50); scores.entry(String::from("Blue")).or_insert(50); println!("{:?}", scores); }
Cargo.toml
文件进行描述如何构建内含的cratescargo new
创建packagesrc/main.rs
是Rust package中默认的crate root::
调用模块子方法(或枚举或struct
),使用pub
进行模块公开,默认模块及模块方法全私有。self
调用自身模块的方法,super
调用父模块的方法。as
进行别名use std::fmt::Result; use std::io::Result as IoResult; fn function1() -> Result { // --snip-- } fn function2() -> IoResult<()> { // --snip-- }
// (1) pub use terminal::Terminal; fn main() { // xxx }
// (2) // 可以直接使用crate的Terminal,其实应该写成 use crate::terminal::Terminal // 但是因为main.rs内部使用了pub use terminal::Terminal, // 相当于将terminal下的Terminal暴露至了crate级别 // 类似js中index.js全部re-export,所有子模块就可以直接从入口处统一导入 use crate::Terminal;
pub struct Size { pub width: u16, pub height: u16, } pub struct Terminal { size: Size, } impl Terminal { pub fn default() -> Result<Self, std::io::Error> { // xxx } pub fn get_size(&self) -> &Size { &self.size } }
panic!
应对不可恢复类型异常fn main() { // a manual panic! call // panic!("plz panic!"); let vec = [1, 2, 3]; // this will break the program vec[99]; }
[profile.release] panic = 'abort'
Result
来应对可恢复类型异常fn try_open_file_with_specific_errors() { let greeting_file_result = File::open("hello.txt"); let greeting_file = match greeting_file_result { Ok(file) => file, Err(error) => match error.kind() { // 如果是NotFound的Kind,直接生成文件。 ErrorKind::NotFound => match File::create("hello.txt") { Ok(fc) => fc, Err(e) => panic!("Problem creating the file: {:?}", e), }, other_error => { panic!("Problem opening the file: {:?}", other_error); } }, }; println!("success!, {:#?}", greeting_file) }
Result
相关的方法可以简化代码,例如使用unwrap
以及expect
use std::fs::File; fn main() { let greeting_file = File::open("hello.txt").unwrap(); let greeting_file = File::open("hello.txt") .expect("hello.txt should be included in this project"); }
Result
结果是Ok
,unwrap
则会直接返回Result
内的值,否则会调用panic!
。使用expect
可以填入自定义的panic信息,报错信息会更加语义化。推荐使用expect
Err
传递。use std::fs::File; use std::io::{self, Read}; fn read_username_from_file() -> Result<String, io::Error> { let username_file_result = File::open("hello.txt"); let mut username_file = match username_file_result { Ok(file) => file, Err(e) => return Err(e), }; let mut username = String::new(); match username_file.read_to_string(&mut username) { Ok(_) => Ok(username), Err(e) => Err(e), } }
Err
分支内直接将错误返回,注意函数返回类型。Result
以及match
use std::fs::File; use std::io::{self, Read}; fn read_username_from_file() -> Result<String, io::Error> { let mut username_file = File::open("hello.txt")?; let mut username = String::new(); username_file.read_to_string(&mut username)?; Ok(username) }
Result
后标明?
代表若有错误,直接返回一个Err
的Result
至外部。Result
后带有?
则表示会对外界传递Err
,返回类型一定要注意写。trait Summary { fn summarize(&self) -> String; }
pub struct NewsArticle { pub headline: String, pub location: String, pub author: String, pub content: String, } 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, } impl Summary for Tweet { fn summarize(&self) -> String { format!("{}: {}", self.username, self.content) } }
pub trait Summary { fn summarize(&self) -> String { String::from("(Read more...)") } }
fn need_to_impl_summary<T: Summary>(item: &T) { item.summarize() } // 缩略版 fn need_to_impl_summary_simple(item: &impl Summary) { item.summarize() }
pub fn notify<T: Summary + Display>(item: &T) {} pub fn notify(item: &(impl Summary + Display)) {}
where
关键字fn some_function<T, U>(t: &T, u: &U) -> i32 where T: Display + Clone, U: Clone + Debug, {}
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, } }
Summary
接口,这里有一个隐形的限制就是返回类型必须得是一种。必能返回2种不同的struct
,但是都实现了Sumary
Lifetimes are another kind of generic that we’ve already been using. Rather than ensuring that a type has the behavior we want, lifetimes ensure that references are valid as long as we need them to be.
fn main() { let r; { let x = 5; r = &x; } println!("r: {}", r); }
r
被赋值一个无效的指针x
。$ cargo run Compiling chapter10 v0.1.0 (file:///projects/chapter10) error[E0597]: `x` does not live long enough --> src/main.rs:6:13 | 6 | r = &x; | ^^ borrowed value does not live long enough 7 | } | - `x` dropped here while still borrowed 8 | 9 | println!("r: {}", r); | - borrow later used here For more information about this error, try `rustc --explain E0597`. error: could not compile `chapter10` due to previous error
fn main() { let r; // ---------+-- 'a // | { // | let x = 5; // -+-- 'b | r = &x; // | | } // -+ | // | println!("r: {}", r); // | } // ---------+
r
的引用存活范围在’a
,x
的引用存活范围在’b
’a
是远大于’b
的,也就是说x
的存活时间没有活到’a
结束。这就是borrow checker的原理。fn main() { let x = 5; // ----------+-- 'b // | let r = &x; // --+-- 'a | // | | println!("r: {}", r); // | | // --+ | } // ----------+
x
变量可以跟随r
一直到末尾。也就满足x
的存活时间活到了r
结束。borrow checker检查通过。fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y } }
&i32 // a reference &'a i32 // a reference with an explicit lifetime &'a mut i32 // a mutable reference with an explicit lifetime
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }
x
和y
一致,即只要x
y
存在,返回值就存在。struct ImportantExcerpt<'a> { part: &'a str, } fn main() { let novel = String::from("Call me Ishmael. Some years ago..."); let first_sentence = novel.split('.').next().expect("Could not find a '.'"); let i = ImportantExcerpt { part: first_sentence, }; }
fn first_word(s: &str) -> &str
将变成 fn first_word<'a>(s: &'a str) -> &str
fn first_word<'a>(s: &'a str) -> &str
将变成fn first_word<'a>(s: &'a str) -> &'a str
let s: &'static str = "I have a static lifetime.";
let example_closure = |x| x; let s = example_closure(String::from("hello")); // 报错,Rustc会根据第一次调用进行类型推断,第二次的类型与第一次不同 let n = example_closure(5);
let list = vec![1, 2, 3]; println!("Before defining closure: {:?}", list); // 使用闭包就不报错 let only_borrows = || println!("From closure: {:?}", list); fn only_borrows() { // list报错,can't capture dynamic environment in a fn item println!("From function: {:?}", list); } println!("Before calling closure: {:?}", list); only_borrows(); println!("After calling closure: {:?}", list);
metaprogramming
。println!
which could take any number of arguments, depending on the format string.macro_rules!
关键字定义,可以在代码中匹配和转换模式。而函数使用 fn
关键字定义,并遵循 Rust 函数的语法规则。metaprogramming
)println!
宏就是一个常见的宏。在编写代码时,你可以使用 println!("Hello, {}!", name)
来打印带有变量 name
值的消息。在宏展开时,println!
宏会根据提供的参数生成对应的代码,以在运行时打印消息。这样,你可以在编写代码时使用宏来简化打印操作,而不必手动编写打印代码的重复代码。Parallel要求有多个可处理的CPU核心,而Concurrent要求多线程。如果没有多核CPU来承载多个任务执行,就不能以Parallel来解决问题。
thread::spawn
返回一个JoinHandle, 可以等待当前线程结束或do something elseuse std::thread; use std::time::Duration; fn main() { let handle = thread::spawn(|| { for i in 1..10 { println!("hi number {} from the spawned thread!", i); thread::sleep(Duration::from_millis(1)); } }); for i in 1..5 { println!("hi number {} from the main thread!", i); thread::sleep(Duration::from_millis(1)); } // join会等待线程的结束,会成功打印1-9,若没有则会答应出1-4(因为主线程提前退出) handle.join().unwrap(); }
use std::thread; use std::time::Duration; fn main() { let handle = thread::spawn(|| { for i in 1..10 { println!("hi number {} from the spawned thread!", i); thread::sleep(Duration::from_millis(1)); } }); // 若放在中间,join会等待新thread完全运行完成再执行主线程代码 handle.join().unwrap(); for i in 1..5 { println!("hi number {} from the main thread!", i); thread::sleep(Duration::from_millis(1)); } }
cargo package
检查打包后的crate文件cargo publish
cargo.toml
区分release与dev的profile设置[profile.*]
语法区分环境,例如[profile.dev] opt-level = 0 [profile.release] opt-level = 3
///
来表示注释为Rust Doc。例如://! # My Crate //! //! `my_crate` is a collection of utilities to make performing certain //! calculations more convenient. /// Adds one to the number given. /// /// # Examples /// /// ``` /// let arg = 5; /// let answer = my_crate::add_one(arg); /// /// assert_eq!(6, answer); /// ``` pub fn add_one(x: i32) -> i32 { x + 1 }
cargo doc
生成doc,使用cargo doc —open
打开文档预览。assert_eq!
会作为一个独立的测试用例在使用cargo test
时被调用检测//!
表示当前包的信息,会显示在文档最上方。cargo login
: https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#setting-up-a-cratesio-accountcargo login xxxxxxxxxxyourapitokenxxxx --registry crates-io
—registry crates-io
的命令行设置,可以通过建立.cargo/config.toml
并声明默认发布registry,例如:# .cargo/config.toml [registry] default = "crates-io"
cargo clippy -- -W clippy::pedantic
针对所有lint rules的一次校验cargo clean
清理build文件,让lint作用于所有文件let results = if config.ignore_case { search_case_insensitive(&config.query, &contents) } else { search(&config.query, &contents) };
let results = match config.ignore_case { true => search_case_insensitive(&config.query, &contents), false => search(&config.query, &contents), };
println!( "The spawned thread is {}", if let true = handler.is_finished() { "finished" } else { "not finished" } );
saturating_add
let width = 5; let sliced = &String::from("xxxxxxxx")[..width]
macro_rules! print_env { ($name: expr) => { println!("{}={}", $name, env!($name)) }; } print_env!("CARGO_PKG_VERSION_MAJOR"); print_env!("CARGO_MANIFEST_DIR"); print_env!("CARGO_PKG_AUTHORS"); print_env!("CARGO_PKG_DESCRIPTION"); print_env!("CARGO_PKG_HOMEPAGE"); print_env!("CARGO_PKG_NAME"); print_env!("CARGO_PKG_REPOSITORY"); print_env!("CARGO_PKG_VERSION"); print_env!("CARGO_PKG_VERSION_MAJOR"); print_env!("CARGO_PKG_VERSION_MINOR"); print_env!("CARGO_PKG_VERSION_PATCH"); print_env!("CARGO_PKG_VERSION_PRE");
cargo new
创建新的cargo,cargo run
运行cargocrago doc —-open
会打开当前crate内相关引用crate的文档Cargo.toml
并声明workspaces-members 参考:https://github.com/citrus327/give-rust-one-more-chance/blob/main/Cargo.tomlcargo run
,或者在project root使用cargo run --bin [cargo-name]
来执行turbofish
语法 (涡轮鱼)::<>
就是turbofish。常用于不给变量进行类型定义,而是使用 turbofish 在构造器上直接标注类型let b = Vec::<bool>::new(); // 使用turbofish语法直接表明b的类型 let b: Vec<bool> = Vec::new(); // explicit表明类型