The type system in Rust is one of its core features, supporting a range of different types and providing a powerful system for static type checking.
In Rust, the type of all variables is determined at compile time. This means that if you try to use a value as a different type at runtime, Rust’s compiler will throw an error at the compile stage, not at runtime.
Here are some of the main types in Rust:
Primitive Types: These include integer types (like i32, u32), floating-point types (f32, f64), boolean (bool), and character (char).
Compound Types: These include tuples and arrays. Tuples are a collection of values of different types, while arrays are a collection of values of the same type.
String Types: Rust has two main string types, String
and str
. String
is a growable, heap-allocated string type, while str
typically exists as a slice (which represents a fixed-size string).
Smart Pointer Types: Such as Box
, Rc
, and RefCell
. These types provide advanced features for memory safety and management.
Custom Types: You can define your own types using the struct
keyword or define an enumeration type using the enum
keyword.
Function Types: Functions are also a type that can be passed as parameters or returned from other functions.
Trait Types: Rust uses traits to define shared behavior across multiple types. They’re similar to interfaces in other languages (like Java interfaces).
Option and Result Types: These two types are fundamental to how Rust handles nullability and error handling.
The above is an overview of the Rust type system, but there’s much more depth and detail. Rust’s type system makes it an incredibly powerful and safe language, catching many type errors at compile time, and preventing many common runtime errors.
fn main() {
let spend = 1;
// 错误!不提供隐式转换
// let cost: f64 = spend; // mismatched types expected `f64`, found integer
// 可以显式转换
let cost = spend as f64;
println!("转换 {} -> {}", spend, cost); // 转换 1 -> 1
// 带后缀的字面量,其类型在初始化时已经知道了
let x = 1u8;
let y = 2u32;
let z = 3f32;
// 无后缀的字面量,编译器有默认类型
let i = 1;
let f = 1.0;
let study = String::from("Rust");
let mut vec = Vec::new();
vec.push(study);
println!("{:?}", vec); // ["Rust"]
// 别名 要用驼峰命名法CamelCase
// 别名不是新类型,不提供额外的类型安全
let myU64:MyU64 = 5 as ThirdU64;
let otherU64:OtherU64 = 2 as ThirdU64;
println!("{} MyU64 + {} OtherU64 = {}", myU64, otherU64, myU64 + otherU64); // 5 MyU64 + 2 OtherU64 = 7
}
type MyU64 = u64;
type OtherU64 = u64;
type ThirdU64 = u64;
It is worth noting that in Rust, functions are also treated as a type and can be assigned to variables, passed as function parameters, and returned from other functions. This supports the concept of first-class functions and higher-order functions in the language.
Here’s a simple example:
fn main() {
let greeter: fn(&str) -> String = create_greeting;
println!("{}", greeter("World"));
}
fn create_greeting(name: &str) -> String {
format!("Hello, {}!", name)
}
In this example, greeter
is a variable of a function type. It is assigned to the function create_greeting
, which takes a reference to a string and returns a new String
. Later on, greeter
can be used just like a regular function.
You can also use function types to define higher-order functions, i.e., functions that take functions as parameters or return functions as results. Here’s an example of a higher-order function:
fn main() {
let result = apply_twice(add_three, 7);
println!("{}", result); // 13
}
fn apply_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(f(arg))
}
fn add_three(x: i32) -> i32 {
x + 3
}
In this example, apply_twice
is a higher-order function. It takes a function f
and an argument arg
, applies f
to arg
twice, and returns the result.