Struct的基本使用

使用struct关键字定义Struct类型。

具名Struct

具名Struct(named Struct)表示有字段名称的Struct。Struct的字段(Field)也可以称为Struct的属性(Attribute)。

例如,定义一个名为Person的Struct结构体,Person包含三个属性,分别是name、age和email,每个属性都需要指定数据类型,这样可以限制各属性允许存放什么类型的数据。

#![allow(unused)]
fn main() {
struct Person{
  name: String,
  age: u32,
  email: String, // 最后一个字段的逗号可省略,但建议保留
}
}

定义Struct后,可创建Struct的实例对象,为其各个属性指定对应的值即可。

例如,构造Person结构体的实例对象user1,

#![allow(unused)]
fn main() {
let user1 = Person {
  name: String::from("junmajinlong"),
  email: String::from("[email protected]"),
  age: 23,
};
}

创建user1实例对象后,可以通过user1.name访问它的name字段的值,user1.age访问它的age字段的值。

以下是一段完整的代码:

struct Person{
  name: String,
  age: u32,
  email: String,
}

fn main(){
  let user1 = Person{
    name: String::from("junmajinlong"),
    email: String::from("[email protected]"),
    age: 23,
  };
  // 访问user1实例name字段、age字段和email字段的值
  println!(
    "name: {}, age: {}, email: {}",
    user1.name, user1.age, user1.email
  );
}

构造struct的简写方式

当要构造的Struct实例的字段值来自于变量,且这个变量名和字段名相同,则可以简写该字段。

struct Person{
  name: String,
  age: u32,
  email: String,
}

fn main(){
  let name = String::from("junmajinlong");
  let email = String::from("[email protected]");

  let user1 = Person{
    name,      // 简写,等价于name: name
    email,     // 简写,等价于email: email
    age: 23,
  };
}

有时候会基于一个Struct实例构造另一个Struct实例,Rust允许通过..xx的方式来简化构造struct实例的写法:

#![allow(unused)]
fn main() {
let name = String::from("junmajinlong");
let email = String::from("[email protected]");
let user1 = Person{
  name,
  email,
  age: 23,
};

let mut user2 = Person{
  name: String::from("gaoxiaofang"),
  email: String::from("[email protected]"),
  ..user1
};
}

上面的..user1表示让user2借用或拷贝user1的某些字段值,由于user2中已经手动定义了name和email字段,因此..user1只借用了user1的age字段,即user2.age也是23。

注意,如果..base借用于base的字段是可Copy的,那么在借用时会自动Copy,这样在借用字段之后,base中的字段仍然有效。但如果借用的字段不是Copy的,那么在借用时会将base中字段的所有权转移走,使得base中的该字段无效。

例如,同时借用user1中的age字段和email字段,由于age是i32类型,是Copy的,所以user1.age仍然可用,但由于String类型不是Copy的,所以user1.email不可用。

#![allow(unused)]
fn main() {
let name = String::from("junmajinlong");
let email = String::from("[email protected]");
let user1 = Person{
  name,
  email,
  age: 23,
};

let mut user2 = Person{
  name: String::from("gaoxiaofang"),
  ..user1
};

// 报错,user1.email字段值的所有权已借给user2
// println!("{}", user1.email);
// println!("{}", user1);       // 报错
println!("{}", user1.name);     // 正确
println!("{}", user1.age);      // 正确
}

如果确实要借用user1的email属性,可以使用..user1.clone()先拷贝堆内存中的user1,这样就不会借用原始的user1中的email所有权。

#![allow(unused)]
fn main() {
let user2 = Person{
  name: String::from("ggg"),
  ..user1.clone()
}
}

tuple struct

除了named struct外,Rust还支持没有字段名的struct结构体,称为元组结构体(tuple struct)。

例如:

#![allow(unused)]
fn main() {
struct Color(i32, i32, i32); 
struct Point(i32, i32, i32); 

let black = Color(0, 0, 0); 
let origin = Point(0, 0, 0);
}

black和origin值的类型不同,因为它们是不同的结构体的实例。在其他方面,元组结构体实例类似于元组:可以将其解构,也可以使用.后跟索引来访问单独的值,等等。

unit-like struct

类单元结构体(unit-like struct)是没有任何字段的空struct。

#![allow(unused)]
fn main() {
struct St;
}