类型别名
类型别名的意思是 "给某个类型一个新的名字"。类型别名非常简单。通常,当您有一个很长的类型,而又不想每次都写它时,您就会使用它们。当您想给一个类型起一个更好的名字,便于记忆时,也可以使用它。下面是两个类型别名的例子。
这里是一个不难的类型,但是你想让你的代码更容易被其他人(或者你)理解。
type CharacterVec = Vec<char>; fn main() {}
这是一种非常难读的类型:
// this return type is extremely long fn returns<'a>(input: &'a Vec<char>) -> std::iter::Take<std::iter::Skip<std::slice::Iter<'a, char>>> { input.iter().skip(4).take(5) } fn main() {}
所以你可以改成这样。
type SkipFourTakeFive<'a> = std::iter::Take<std::iter::Skip<std::slice::Iter<'a, char>>>; fn returns<'a>(input: &'a Vec<char>) -> SkipFourTakeFive { input.iter().skip(4).take(5) } fn main() {}
当然,你也可以导入元素,让类型更短:
use std::iter::{Take, Skip}; use std::slice::Iter; fn returns<'a>(input: &'a Vec<char>) -> Take<Skip<Iter<'a, char>>> { input.iter().skip(4).take(5) } fn main() {}
所以你可以根据自己的喜好来决定在你的代码中什么是最好看的。
请注意,这并没有创建一个实际的新类型。它只是一个代替现有类型的名称。所以如果你写了 type File = String;
,编译器只会看到 String
。所以这将打印出 true
。
type File = String; fn main() { let my_file = File::from("I am file contents"); let my_string = String::from("I am file contents"); println!("{}", my_file == my_string); }
那么如果你想要一个实际的新类型呢?
如果你想要一个新的文件类型,而编译器看到的是File
,你可以把它放在一个结构中。
struct File(String); // File is a wrapper around String fn main() { let my_file = File(String::from("I am file contents")); let my_string = String::from("I am file contents"); }
现在这样就不行了,因为它们是两种不同的类型。
struct File(String); // File is a wrapper around String fn main() { let my_file = File(String::from("I am file contents")); let my_string = String::from("I am file contents"); println!("{}", my_file == my_string); // ⚠️ cannot compare File with String }
如果你想比较里面的String,可以用my_file.0:
struct File(String); fn main() { let my_file = File(String::from("I am file contents")); let my_string = String::from("I am file contents"); println!("{}", my_file.0 == my_string); // my_file.0 is a String, so this prints true }
在函数中导入和重命名
通常你会在程序的顶部写上use
,像这样。
use std::cell::{Cell, RefCell}; fn main() {}
但我们看到,你可以在任何地方这样做,特别是在函数中使用名称较长的enum。下面是一个例子:
enum MapDirection { North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest, } fn main() {} fn give_direction(direction: &MapDirection) { match direction { MapDirection::North => println!("You are heading north."), MapDirection::NorthEast => println!("You are heading northeast."), // So much more left to type... // ⚠️ because we didn't write every possible variant } }
所以现在我们要在函数里面导入MapDirection。也就是说,在函数里面你可以直接写North
等。
enum MapDirection { North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest, } fn main() {} fn give_direction(direction: &MapDirection) { use MapDirection::*; // Import everything in MapDirection let m = "You are heading"; match direction { North => println!("{} north.", m), NorthEast => println!("{} northeast.", m), // This is a bit better // ⚠️ } }
我们已经看到::*
的意思是 "导入::之后的所有内容"。在我们的例子中,这意味着North
,NorthEast
......一直到NorthWest
。当你导入别人的代码时,你也可以这样做,但如果代码非常大,你可能会有问题。如果它有一些元素和你的代码是一样的呢?所以一般情况下最好不要一直使用::*
,除非你有把握。很多时候你在别人的代码里看到一个叫prelude
的部分,里面有你可能需要的所有主要元素。那么你通常会这样使用:name::prelude::*
。 我们将在 modules
和 crates
的章节中更多地讨论这个问题。
您也可以使用 as
来更改名称。例如,也许你正在使用别人的代码,而你不能改变枚举中的名称。
enum FileState { CannotAccessFile, FileOpenedAndReady, NoSuchFileExists, SimilarFileNameInNextDirectory, } fn main() {}
那么你就可以
- 导入所有的东西
- 更改名称
enum FileState { CannotAccessFile, FileOpenedAndReady, NoSuchFileExists, SimilarFileNameInNextDirectory, } fn give_filestate(input: &FileState) { use FileState::{ CannotAccessFile as NoAccess, FileOpenedAndReady as Good, NoSuchFileExists as NoFile, SimilarFileNameInNextDirectory as OtherDirectory }; match input { NoAccess => println!("Can't access file."), Good => println!("Here is your file"), NoFile => println!("Sorry, there is no file by that name."), OtherDirectory => println!("Please check the other directory."), } } fn main() {}
所以现在你可以写OtherDirectory
而不是FileState::SimilarFileNameInNextDirectory
。