复合类型
行百里者半九十,欢迎大家来到这里,虽然还不到中点,但是已经不远了。如果说之前学的基础数据类型是原子,那么本章将讲的数据类型可以认为是分子。
本章的重点在复合类型上,顾名思义,复合类型是由其它类型组合而成的,最典型的就是结构体 struct
和枚举 enum
。例如平面上的一个点 point(x, y)
,它由两个数值类型的值 x
和 y
组合而来。我们无法单独去维护这两个数值,因为单独一个 x
或者 y
是含义不完整的,无法标识平面上的一个点,应该把它们看作一个整体去理解和处理。
来看一段代码,它使用我们之前学过的内容来构建文件操作:
#![allow(unused_variables)] type File = String; fn open(f: &mut File) -> bool { true } fn close(f: &mut File) -> bool { true } #[allow(dead_code)] fn read(f: &mut File, save_to: &mut Vec<u8>) -> ! { unimplemented!() } fn main() { let mut f1 = File::from("f1.txt"); open(&mut f1); //read(&mut f1, &mut vec![]); close(&mut f1); }
接下来我们的学习非常类似原型设计:有的方法只提供 API 接口,但是不提供具体实现。此外,有的变量在声明之后并未使用,因此在这个阶段我们需要排除一些编译器噪音(Rust 在编译的时候会扫描代码,变量声明后未使用会以 warning
警告的形式进行提示),引入 #![allow(unused_variables)]
属性标记,该标记会告诉编译器忽略未使用的变量,不要抛出 warning
警告,具体的常见编译器属性你可以在这里查阅:编译器属性标记。
read
函数也非常有趣,它返回一个 !
类型,这个表明该函数是一个发散函数,不会返回任何值,包括 ()
。unimplemented!()
告诉编译器该函数尚未实现,unimplemented!()
标记通常意味着我们期望快速完成主要代码,回头再通过搜索这些标记来完成次要代码,类似的标记还有 todo!()
,当代码执行到这种未实现的地方时,程序会直接报错。你可以反注释 read(&mut f1, &mut vec![]);
这行,然后再观察下结果。
同时,从代码设计角度来看,关于文件操作的类型和函数应该组织在一起,散落得到处都是,是难以管理和使用的。而且通过 open(&mut f1)
进行调用,也远没有使用 f1.open()
来调用好,这就体现出了只使用基本类型的局限性:无法从更高的抽象层次去简化代码。
接下来,我们将引入一个高级数据结构 —— 结构体 struct
,来看看复合类型是怎样更好的解决这类问题。 开始之前,先来看看 Rust 的重点也是难点:字符串 String
和 &str
。