变量和常量
变量声明
在 Rust 中,变量默认是不可变的。这意味着一旦一个变量被赋予了一个值,它就不能再被改变。你可以使用 let 关键字来声明一个变量:
let x = 5;
如果你想声明一个可变变量,你需要在 let 关键字后面加上 mut 关键字:
let mut y = 10; y = 20; // 现在 y 的值为 20
常量声明
常量是不可变的,并且必须在声明时初始化。常量可以使用任何类型,但它们的类型必须能被推断出来或者明确指定。常量通常用于表示不会改变的值。声明常量需要使用 const 关键字:
const MAX_POINTS: u32 = 100_000;
类型注解
虽然 Rust 有自动类型推断的能力,但在某些情况下,你可能需要显式地指定变量的类型。这可以通过类型注解来实现。例如:
let guess: u32 = "42".parse().expect("Not a number!");在这个例子中,guess 被明确地指定为 u32 类型。
数据类型
基本数据类型
Rust 提供了多种基本数据类型,包括整型、浮点型、布尔型和字符型。
整型
Rust 提供了多种有符号和无符号的整型,每种都有不同的位数。常见的有 i8, i16, i32, i64, i128 以及 u8, u16, u32, u64, u128。例如:
let x: i32 = -10; let y: u32 = 100;
浮点型
Rust 有两种浮点类型:f32 和 f64。其中 f64 是默认类型。例如:
let pi = 3.141592; let e = 2.71_f32;
布尔型
布尔类型只有两种取值:true 和 false。例如:
let is_raining = true; let is_sunny = false;
字符型
Rust 使用 char 类型来表示单个 Unicode 字符。字符必须用单引号包围。例如:
let c = 'z'; let z = 'ℤ'; let heart_eyed_cat = '😻';
复合类型
元组
元组是一种将多个不同类型的数据组合在一起的方式。元组中的元素可以是任意类型,但它们的类型和数量在定义时必须确定。例如:
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {}", y);数组
数组是一系列相同类型的值。数组的长度在编译时确定,不能改变。例如:
let a: [i32; 5] = [1, 2, 3, 4, 5]; let b = [3; 5]; // 这是一个包含五个 3 的数组
函数
函数定义
函数在 Rust 中通过 fn 关键字定义。函数可以接受参数,并且可以返回一个值。例如:
fn main() {
println!("Hello, world!");
}
fn add_one(x: i32) -> i32 {
x + 1
}在这个例子中,add_one 函数接受一个 i32 类型的参数并返回一个 i32 类型的值。
参数和返回值
函数可以接受任意数量的参数,每个参数都需要指明类型。同样,函数也可以返回任意类型的值,返回类型也需要在函数签名中声明。例如:
fn plus_one(x: i32) -> i32 {
x + 1
}控制流
Rust 支持多种控制流语句,如 if、else、loop、while 和 for 循环等。
if 语句
if 语句允许根据条件执行代码块。例如:
// www.javascriptcn.com code example
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");
}while 循环
while 循环允许重复执行一段代码直到条件不再满足。例如:
let mut count = 0;
while count < 5 {
println!("{}", count);
count += 1;
}for 循环
for 循环提供了一种遍历集合(如数组或向量)的方法。例如:
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
println!("the value is: {}", element);
}结构体和枚举
结构体
结构体是一种用户自定义的数据类型,允许你将一组相关的字段组合在一起。例如:
// www.javascriptcn.com code example
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn build_user(email: String, username: String) -> User {
User {
email,
username,
active: true,
sign_in_count: 1,
}
}
let user1 = build_user(String::from("someone@example.com"), String::from("someusername123"));枚举
枚举允许你定义一个类型由一组可能的值组成。例如:
// www.javascriptcn.com code example
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
impl Message {
fn call(&self) {
// 方法体可以为空
}
}
let m = Message::Write(String::from("hello"));
m.call();模块系统
Rust 使用模块系统来组织代码。模块帮助你管理命名空间,并且可以用来封装代码。例如:
// www.javascriptcn.com code example
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub use crate::front_of_house::hosting;
fn serve_order() {}
mod back_of_house {
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String,
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
pub enum Appetizer {
Soup,
Salad,
}
}
fn eat_at_restaurant() {
hosting::add_to_waitlist();
let mut meal = back_of_house::Breakfast::summer("Rye");
meal.toast = String::from("Wheat");
println!("I'd like {} toast please", meal.toast);
let order1 = back_of_house::Appetizer::Soup;
let order2 = back_of_house::Appetizer::Salad;
}在这个例子中,front_of_house 和 back_of_house 都是模块,hosting 是 front_of_house 的子模块。eat_at_restaurant 函数展示了如何使用这些模块中的类型和函数。
错误处理
Result 类型
Rust 使用 Result 类型来处理错误。Result 是一个枚举,有两种可能的状态:Ok 和 Err。例如:
// www.javascriptcn.com code example
use std::fs::File;
fn read_username_from_file() -> Result<String, std::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_file_short() -> Result<String, std::io::Error> {
let mut f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}在这个例子中,read_username_from_file 函数展示了如何使用 match 来处理 Result 类型的返回值。而 read_username_from_file_short 函数则使用了更简洁的 ? 操作符来处理错误。
泛型
泛型函数
泛型允许你在不指定具体类型的情况下编写代码。这样可以使你的代码更加通用和复用性更高。例如:
// www.javascriptcn.com code example
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list.iter() {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("The largest number is {}", result);
let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest(&char_list);
println!("The largest char is {}", result);
}在这个例子中,largest 函数接受一个泛型参数 T,并且要求 T 实现 PartialOrd 和 Copy 特征。这意味着这个函数可以用来找出任何实现了这些特征的类型的最大值。
泛型结构体
你也可以为结构体定义泛型。例如:
// www.javascriptcn.com code example
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("p.x = {}", p.x());
let p2 = Point { x: 1.0, y: 4.0 };
println!("distance from origin: {}", p2.distance_from_origin());
}在这个例子中,Point 结构体是一个泛型结构体,可以存储任意类型的 x 和 y 坐标。对于特定的 f32 类型,我们还为其定义了一个方法 distance_from_origin,用于计算原点到该点的距离。
生命周期
生命周期注解
生命周期注解确保引用在其作用域内始终有效。这对于避免悬垂引用非常重要。例如:
// www.javascriptcn.com code example
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("long string is long");
{
let string2 = String::from("xyz");
let result = longest(string1.as_str(), string2.as_str());
println!("The longest string is {}", result);
}
}在这个例子中,longest 函数接受两个字符串切片引用作为参数,并返回一个引用。生命周期注解 'a 确保返回的引用与输入的引用具有相同的生命周期。
生命周期省略规则
Rust 编译器会尝试应用一些生命周期省略规则,以减少显式生命周期注解的需求。例如:
// www.javascriptcn.com code example
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
fn main() {
let my_string = String::from("hello world");
// first_word works on slices of `String`s
let word = first_word(&my_string[..]);
println!("The first word is: {}", word);
}在这个例子中,first_word 函数接受一个字符串切片引用,并返回一个字符串切片引用。由于 &str 类型的生命周期是隐式的,所以不需要显式地添加生命周期注解。
Trait
定义 Trait
Trait 是一种定义共享行为的方式。它们类似于 Java 中的接口。例如:
// www.javascriptcn.com code example
trait Summary {
fn summarize(&self) -> String;
}
struct NewsArticle {
headline: String,
location: String,
author: String,
content: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
struct Tweet {
username: String,
content: String,
reply: bool,
retweet: bool,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
fn main() {
let article = NewsArticle {
headline: String::from("Penguins win the Stanley Cup Championship!"),
location: String::from("Pittsburgh, PA, USA"),
author: String::from("Iceburgh"),
content: String::from("Two-time defending NHL champions Pittsburgh Penguins are back with another Stanley Cup victory."),
};
println!("New article available! {}", article.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());
}在这个例子中,Summary Trait 定义了一个 summarize 方法。然后,我们为 NewsArticle 和 Tweet 类型实现了这个 Trait。
组合 Trait
你可以通过在 Trait 中包含其他 Trait 来组合它们。例如:
// www.javascriptcn.com code example
use std::fmt::Display;
trait OutlinePrint: Display {
fn outline_print(&self) {
let output = self.to_string();
let len = output.len();
println!("{}", "*".repeat(len + 4));
println!("*{}*", " ".repeat(len + 2));
println!("* {} *", output);
println!("*{}*", " ".repeat(len + 2));
println!("{}", "*".repeat(len + 4));
}
}
struct Point {
x: i32,
y: i32,
}
impl Display for Point {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
impl OutlinePrint for Point {}
fn main() {
let p = Point { x: 10, y: 20 };
p.outline_print();
}在这个例子中,OutlinePrint Trait 包含了 Display Trait。因此,任何实现了 OutlinePrint Trait 的类型也必须实现 Display Trait。通过这种方式,你可以将多个相关的行为组合在一起,使代码更加模块化和易于理解。
以上就是 Rust 的基本语法概述。希望这些示例和解释能帮助你更好地理解和掌握 Rust 的核心概念。