一. 基本介绍虽然是静态类型语言,但是定义变量,不用定义类型,只用关键字声明即可,即用关键字 let ,Rust 有类型推断,用以平衡强大的静态类型和冗长标注类型。
let x = "hello, world!";
let a="foobar";
let b="foo\
bar";
let mut x = vec!["Hello", "world"]; 使用名字叫“vec”的宏(感叹号表示宏),将这个宏绑定到一个叫“x”的变量上。
Rust 更倾向于堆栈分配而不是堆分配:x 被直接放置在堆栈中。然而,Vec 类型是在堆上分配的向量元素的空间。
这个let体现的是Rust中的“所有权”概念。
我们将Rust语言类型分为两种来说明,即基本类型和集合。
二. 基本类型
1.字符和字符串
字符类型 :let a = 'b';
字符串类型 : let a = "abcdef";
行类型 : let a = r#abcdef#;
字节字符类型 :let a = b'a';
字节字符串类型:let a = b"abcdef";
字节行类型 : let a = br#abcdef#;
前缀b=bite字节, r=row行
\字符串换行符,也是转义字符的标识。
let a = "foobar";
let b = "foo\
bar";
a和b是相等的字符串。
Rust包含2个字符串类型:String 和 &str
A. &str称为“字符串切片”,固定大小,并且不可改变(string slice )。
比如: let a = "hello world" //那么a就是一个字符串切片
"hello world" 是一个“本义字符串”(string literal,即直接双引号内拼写出来的字符串)并且是静态的字符串,一个“本义字符串”属于是一个字符串切片,是静态分配的,它被保存在编译的程序中,在整个程序运行过程都存在,类似java中的静态变量,那么a呢,就是一个绑定,换个说法,就是一个引用,引用那个静态分配的字符串,所有功能想要得到一个字符串切片的,都会得到一个“本义字符串”。
为什么作者要称这个为字符串切片(slice)呢?是因为这种字符是随时可以切成一个一个字符的!
B. String堆分配,可增长,保证是UTF-8编码,通常是由一个字符串切片通过to_string方法强制转换过来的。
let mut s = "Hello".to_string(); // mut s: String
println!("{}", s);
s.push_str(", world."); //增加值,即可增长
println!("{}", s);
使用一个&符号可以将Strings类型强制转换为&str类型
fn takes_slice(slice: &str) {
println!("Got: {}", slice);
}
fn main() {
let s = "Hello".to_string();
takes_slice(&s);
}
关键点:记住String类型分配内存控制它的数据, &str是一个指向另一个字符串的引用,知道这些就可以了。
String 转换成 &str内存成本是比较低的,但是 &str 转换成 String 因为涉及到内存分配,转换成本比较高,所以如果没有必要不要去转换。
usestd::net::TcpStream;
TcpStream::connect("192.168.0.1:3000"); // &str parameterletaddr_string="192.168.0.1:3000".to_string();
TcpStream::connect(&*addr_string); // 转换 addr_string 到 &str
总结:str 是不可变的字符串;
std::String 是可变的字符串;
std::ffi::CStr 用于表示由C分配、rust借用的C字符串;
std::ffi::CString 用于表示由rust分配、可以传递给C函数使用的C字符串;
std::ffi::OsStr 平台相关的字符串,具体看 rust/os_str.rs at master · rust-lang/rust · GitHub;
std::ffi::OsString 这个是上面的可变版本;
std::path::Path 用来表示路径,方法和普通字符串不一样,当然独立出来;
std::path::PathBuf 这是Path的可变版本;
总之普通字符串就用str和String,路径就用Path和PathBuf,其他是ffi才需要用到的。
OsStr/OsString 这两个都是为 Path 而存在
2. 数值
十进制整型(Decimal integer):98_222
十六进制整型(Hex integer) :0xff
八进制整型(Octal integer) :0o77
二进制整型(Binary integer) :0b1111_0000
浮点型(Floating-point) :123.0E+77
数值的后缀
Integer Floating-point
u8, i8, u16, i16, u32, i32, u64, i64, isize, usize f32, f64
数值后缀标明该数值的存储空间
比如整型的数值
123i32; // type i32
123u32; // type u32
123_u32; // type u32
0xff_u8; // type u8
0o70_i16; // type i16
0b1111_1111_1001_0000_i32; // type i32
0usize; // type usize
默认是i32
浮点型的后缀
123.0f64; // type f64
0.1f64; // type f64
0.1f32; // type f32
12E+99_f64; // type f64
let x:f64=2.; // type f64
默认是f64
3. 布尔值
true and false
三、集合
1. 数组特性:固定大小,存放同类型元素,不可变。
let a = [1, 2, 3]; //数组类型是 a: [i32; 3]
let mut m = [1, 2, 3]; //数组类型是 mut m: [i32; 3]
A. 数组类型的表达--[T;N] 其中T指泛型。
B. 数组简单初始化方法:let a = [0; 20]; // a: [i32; 20],即初始化20个元素,每个元素为0。
C. 数组长度:a.len()。
D. 数组遍历:
let a = [1, 2, 3];
println!("a has {} elements", a.len());
for e in a.iter() {
println!("{}", e);
}
E. 数组特定访问:
let names = ["Graydon", "Brian", "Niko"]; // names: [&str; 3]
println!("The second name is: {}", names[1]); // 注意下标也是从0开始
2. 向量特性:可增长的数组, 可变并且自动增长,内存堆分配,类似字符串当中 与&str对应的String,可用宏vec!创建。
let v = vec![1, 2, 3]; // v: Veclet mut nums = vec![1, 2, 3]; // mut nums: Vec
nums.push(4);
println!("The length of nums is now {}", nums.len()); // Prints 4
3. 切片特性:是引用(查看)一个数组部分数据,对于需要安全,高效访问数组的部分数据十分有用,并且不需 要拷贝。例如,你只想将一个文件的一行数据读进内存。
切片不是凭空创建的,而是来自一个存在的变量,切 片有长度,可变或者不可变都可以,使用的方式类似数组:
let a = [0, 1, 2, 3, 4];
let middle = &a[1..4]; // a的切片。包含元素1,2,3 范围是[ ) 实-虚
for e in middle.iter() {
println!("{}", e); // Prints 1, 2, 3
}
4. 元组元组是一个有固定大小的有序列表,里面可以存放任何数据类型的值
let x = (1,"hello") //简单元组
let x: (i32,&str) = (1,"hello"); // 有说明数据类型的元组
let (x,y,z)=(1,2,3); //模糊匹配,同时初始化3个变量
println!("x is {}",x);
A. 元组的分配引用let mut x = (1, 2); // x: (i32, i32)
let y = (2, 3); // y: (i32, i32)
x = y;
B. 元组的判断let x = (1, 2, 3);
let y = (2, 2, 4);
if x == y {
println!("yes");
} else {
println!("no");
}
C. 用于函数返回多个值fn next_two(x: i32) -> (i32, i32) { (x + 1, x + 2) }
fn main() {
let (x, y) = next_two(5);
println!("x, y = {}, {}", x, y);
}