类型
Rust有很多类型,让你可以处理数字、字符等。有些类型很简单,有些类型比较复杂,你甚至可以创建自己的类型。
原始类型
Rust有简单的类型,这些类型被称为原始类型(原始=非常基本)。我们将从整数和char(字符)开始。整数是没有小数点的整数。整数有两种类型。
- 有符号的整数
- 无符号整数
符号是指+(加号)和-(减号),所以有符号的整数可以是正数,也可以是负数(如+8,-8)。但无符号整数只能是正数,因为它们没有符号。
有符号的整数是 i8, i16, i32, i64, i128, 和 isize。
无符号的整数是 u8, u16, u32, u64, u128, 和 usize。
i或u后面的数字表示该数字的位数,所以位数多的数字可以大一些。8位=一个字节,所以i8是一个字节,i64是8个字节,以此类推。尺寸较大的数字类型可以容纳更大的数字。例如,u8最多可以容纳255,但u16最多可以容纳65535。而u128最多可以容纳340282366920938463463374607431768211455。
那么什么是isize和usize呢?这表示你电脑的位数。(你的电脑上的位数叫做你电脑的架构)。所以32位计算机上的isize和usize就像i32和u32,64位计算机上的isize和usize就像i64和u64。
整数类型不同的原因有很多。其中一个原因是计算机性能:较小的字节数处理速度更快。例如,数字-10作为i8是11110110,但作为i128是11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110110。但这里还有一些其他用法。
Rust中的字符叫做char. 每一个char都有一个数字:字母A是数字65,而字符友(中文的 "朋友")是数字21451。这个数字列表被称为 "Unicode"。Unicode对使用较多的字符使用较小的数字,如A到Z,或0到9的数字,或空格。
fn main() { let first_letter = 'A'; let space = ' '; // A space inside ' ' is also a char let other_language_char = 'Ꮔ'; // Thanks to Unicode, other languages like Cherokee display just fine too let cat_face = '😺'; // Emojis are chars too }
使用最多的字符的数字小于256,它们可以装进u8。记住,u8是0加上255以内的所有数字,总共256个。这意味着 Rust 可以使用 as 将 u8 安全地 cast成 char。("把 u8 cast成 char"意味着 "把 u8 假装成 char")
用 as cast是有用的,因为 Rust 是非常严格的。它总是需要知道类型。
而不会让你同时使用两种不同的类型,即使它们都是整数。例如,这将无法工作:
fn main() { // main() is where Rust programs start to run. Code goes inside {} (curly brackets) let my_number = 100; // We didn't write a type of integer, // so Rust chooses i32. Rust always // chooses i32 for integers if you don't // tell it to use a different type println!("{}", my_number as char); // ⚠️ }
原因是这样的:
error[E0604]: only `u8` can be cast as `char`, not `i32`
--> src\main.rs:3:20
|
3 | println!("{}", my_number as char);
| ^^^^^^^^^^^^^^^^^
幸运的是,我们可以用as轻松解决这个问题。我们不能把i32转成char,但我们可以把i32转成u8,然后把u8转换成char。所以在一行中,我们使用 as 将 my_number 变为 u8,再将其变为 char。现在可以编译了。
fn main() { let my_number = 100; println!("{}", my_number as u8 as char); }
它打印的是d,因为那是100对应的char。
然而,更简单的方法是告诉 Rust my_number 是 u8。下面是你的做法。
fn main() { let my_number: u8 = 100; // change my_number to my_number: u8 println!("{}", my_number as char); }
所以这就是Rust中所有不同数字类型的两个原因。这里还有一个原因:usize是Rust用于索引的大小。(索引的意思是 "哪项是第一","哪项是第二"等等)usize是索引的最佳大小,因为:
- 索引不能是负数,所以它需要是一个带u的数字
- 它应该是大的,因为有时你需要索引很多东西,但。
- 不可能是u64,因为32位电脑不能使用u64。
所以Rust使用了usize,这样你的计算机就可以得到它能读到的最大的数字进行索引。
我们再来了解一下char。你看到char总是一个字符,并且使用''而不是""。
所有的 字符 都使用4个字节的内存,因为4个字节足以容纳任何种类的字符:
- 基本字母和符号通常需要4个字节中的1个:
a b 1 2 + - = $ @ - 其他字母,如德语的 Umlauts 或重音,需要4个字节中的2个:
ä ö ü ß è é à ñ - 韩文、日文或中文字符需要3或4个字节:
国 안 녕
当使用字符作为字符串的一部分时,字符串被编码以使用每个字符所需的最小内存量。
我们可以用.len()来看一下。
fn main() { println!("Size of a char: {}", std::mem::size_of::<char>()); // 4 bytes println!("Size of string containing 'a': {}", "a".len()); // .len() gives the size of the string in bytes println!("Size of string containing 'ß': {}", "ß".len()); println!("Size of string containing '国': {}", "国".len()); println!("Size of string containing '𓅱': {}", "𓅱".len()); }
这样打印出来。
Size of a char: 4
Size of string containing 'a': 1
Size of string containing 'ß': 2
Size of string containing '国': 3
Size of string containing '𓅱': 4
可以看到,a是一个字节,德文的ß是两个字节,日文的国是三个字节,古埃及的𓅱是4个字节。
fn main() { let slice = "Hello!"; println!("Slice is {} bytes.", slice.len()); let slice2 = "안녕!"; // Korean for "hi" println!("Slice2 is {} bytes.", slice2.len()); }
这个打印:
Slice is 6 bytes.
Slice2 is 7 bytes.
slice的长度是6个字符,6个字节,但slice2的长度是3个字符,7个字节。
如果.len()给出的是以字节为单位的大小,那么以字符为单位的大小呢?这些方法我们后面会学习,但你只要记住.chars().count()就可以了。.chars().count() 将你写的东西变成字符,然后计算有多少个字符。
fn main() { let slice = "Hello!"; println!("Slice is {} bytes and also {} characters.", slice.len(), slice.chars().count()); let slice2 = "안녕!"; println!("Slice2 is {} bytes but only {} characters.", slice2.len(), slice2.chars().count()); }
这就打印出来了。
Slice is 6 bytes and also 6 characters.
Slice2 is 7 bytes but only 3 characters.