第七章 结构体
结构体类似于其他面向对象语言的类它包含了一些表达某类特性的属性组合内容是一组属性名和属性值的集合。结构体还包含了对应相关联的函数方法和行为。它和元祖的区别是元祖不包含属性名称结构体包含属性名称。元祖使用小括号结构体使用大括号。结构体的定义和实例化使用关键字struct加上名称来制定类型为结构体名称一般使用大写字母开头的英文名称最好是有含义的英文单词或组合其后使用大括号包含属性名称属性名称后面跟着冒号冒号后面指定属性名称的类型每个属性组合之间使用逗号进行分割。具体的示例如下所示struct User { name: String, email: String, active: bool, sign_in_count: u64, }为了使用上面定义的结构体还必须将其实例化即给每一个定义的属性进行赋值。赋值的语法类似定义只不过将属性类型替换为值形成key: value的键值对。属性的赋值顺序不一定要严格按照结构体内定义的顺序。fn main() { let user User { active: true, name: String::from(caocao), email: String::from(caocaoexample.com), sign_in_count: 1, }; println!({user:?}); }如果需要获取结构体中的某个属性的值使用小数点符号“.”来进行声明例如需要访问用户的电子邮件可以使用user.email的方式。如果需要修改属性的值实现化结构体的时候需要将其声明为可变变量使用符号“mut”作为修饰符。如下面的示例所示fn main() { let mut user User { active: true, name: String::from(caocao), email: String::from(caocaoexample.com), sign_in_count: 1, }; println!({0},user.email); user.email String::from(liubeiexample.com); println!({0},user.email) }注意整个结构体实例必须都是声明为可变的Rust不允许只标注某一个属性是可变的。和其他表达式一样我们也可以将实例一个结构体作为一个表达式在某个函数的结尾作为返回值。如下所示fn build_user(email: String, name: String)-User { User { active: true, email: email, name: name, sign_in_count: 1, } }从上面的示例可以看到email: email,name: name,似乎显得有点冗余Rust提供了一种简写形式如果参数名和属性名一致可以只写一个。如下所示fn build_user(email: String, name: String)-User { User { active: true, email, name, sign_in_count: 1, } }是不是简约了许多。两个结构体实例移植的更新语法我们经常会遇到从一个结构体复制一些值到一个新的结构体。这时就可以使用结构体的更新语法。下面的示例是创建user2从user实例内复制一个值没有使用更新语法的示例let user2 User { active: user.active, name: user.name, email: String::from(zhouyuexample.com), sign_in_count: user.sign_in_count, };我们可以使用符号“..”来获取源机构体的其余属性值。下面是使用更新语法的示例let user2 User { email: String::from(zhouyuexample.com), ..user };..user表明该结构体其他属性值来自user。它必须在结构体的最后。使用元祖结构体Rust也提供了一种“元祖结构体”类似与元祖类型但是它使用了struct关键字进行了声明。元祖机构图没有使用属性名而是使用类型来定义成员但是它指定了整个结构的名称用于表明此元祖非彼元祖。可能是如果给每个属性指定名称显得冗长而且多余适用于人们清楚的知道每个成员属性的名称例如颜色值或者坐标值。#[derive(Debug)] struct Color(i32,i32,i32); #[derive(Debug)] struct Point(i32,i32,i32); fn main() { let black Color(0,0,0); let origin Point(0,0,0); println!({black:?}); println!({origin:?}); }注意black和origin是不同的类型因为它们使用了不同的元祖结构进行了实例化。每种结构都定义了它们自己的类型虽然它们的值是一样的且它们的结构体内容也是一致的。你也可以使用元祖的解构方式对元祖机构进行解构例如let Point(x, y, z) origin;。如果要访问器成员也可以使用“.”加上其索引值例如black.0。单元类型的结构体你也可以定义不包含任何属性的结构体我们称之为单元类结构体。这就像类型“()”单元类结构体适用于只需要函数和方法的定义这就像其他语言中的接口。struct AlwaysEqual; fn main() { let subject AlwaysEqual; }我们使用struct定义了结构体AlwaysEqual后面直接使用分号结尾没有花括号也没有属性。如果要实例化直接将其复制给变量就可以了也不用对其的属性进行赋值。结构体中数据的所有权在之前的User结构体中我们使用了String类型而不是str的字符串切片类型这是故意而为之因为我们需要实例化这个User之后拥有它所有数据的所有权这样整个结构体在整个生命周期中的数据都是有效的。当然有时也是需要保存数据的引用值这时就需要使用生命周期的概念了。它可以确保其和结构体一样的生命周期。看看下面使用引用而没有规定其声明周期的示例它在编译期间就会报错struct User { active: bool, name: str, email: str, sign_in_count: u64, } fn main() { let user1 User { active: true, name: caocao, email: caocaoexample.com, sign_in_count: 1, }; }系统报错信息在报错的提示信息中已经提示了如何修改。