Rust 结构体(struct)

Rust 结构体是由一组数据组合而成的一种数据类型。组成结构体的每项数据称为结构体的“成员”。Rust 语言中定义结构体的关键字是 struct。

1. Rust 结构体定义的语法

Rust 结构体是一个 复合类型。结构体中的所有元素/字段必须明确指明它的数据类型。

struct Name_of_structure {
   field1:data_type,
   field2:data_type,
   field3:data_type
}

定义一个结构体时:

  1. 结构体名 Name_of_structure 和元素/字段名 fieldN 遵循普通变量的命名语法。
  2. 结构体中中的每一个元素/字段都必须明确指定数据类型。可以是基本类型,也可以是另一个结构体。

下面的代码,我们定义了一个结构体 Employee ,它有着三个元素/字段,分别是姓名、年龄、公司。

struct Employee {
   name:String,
   company:String,
   age:u32
}

2. 创建结构体的实例

创建结构体的实例或者说结构体初始化本质上就是创建一个变量。使用 let 关键字创建一个变量。

创建结构体的一个实例和定义结构体的语法很类似。结构体实例初始化语法:

let instance_name = Name_of_structure {
    field1:value1,
    field2:value2,
    field3:value3
}; 

从语法中可以看出,初始化结构体时的等号右边,就是把定义语法中的元素类型换成了具体的值。

结构体初始化,其实就是对 结构体中的各个元素进行赋值

注意:千万不要忘记结尾的分号 ;

3. 访问结构体的实例

如果要访问结构体实例的某个元素,我们可以使用 元素访问符,也就是 点号 ( . )

具体的访问语法格式如下

struct_name_instance.field_name

例如,如果要访问 Employee 的实例 emp1 中的 name 元素,可以如下使用

emp1.name

下面的代码,我们定义了一个有三个元素的结构体 Employee,然后初始化一个实例 emp1,最后通过 元素访问符 来访问 emp1 的三个元素。

struct Employee {
   name:String,
   company:String,
   age:u32
}

fn main() {
   let emp1 = Employee {
      company:String::from("TutorialsPoint"),
      name:String::from("Mohtashim"),
      age:50
   };
   println!("Name is :{} company is {} age is {}",emp1.name,emp1.company,emp1.age);
}

编译运行以上 Rust 代码,输出结果如下

Name is :Mohtashim company is TutorialsPoint age is 50

4. 修改结构体的实例

修改结构体实例就是对结构体的个别元素 重新赋值

结构体实例默认是 不可修改的,因为结构体实例也是一个使用 let 定义的变量。

如果要修改结构体实例,就必须在创建时添加 mut 关键字,让它变成可修改的。

因为没啥新的知识内容,我们就直接上范例吧。

下面的范例给 Employee 的实例 emp1 添加了 mut 关键字,因此我们可以修改 emp1 的内部元素。

struct Employee {
   name:String,
   company:String,
   age:u32
}

let mut emp1 = Employee {
   company:String::from("TutorialsPoint"),
   name:String::from("Mohtashim"),
   age:50
};
emp1.age = 40;
println!("Name is :{} company is {} age is 
{}",emp1.name,emp1.company,emp1.age);

编译运行以上 Rust 代码,输出结果如下

Name is :Mohtashim company is TutorialsPoint age is 40

5. 结构体作为函数的参数

结构体的用途之一就是可以作为参数传递给函数。

定一个结构体参数和定义其它类型的参数的语法是一样的。我们这里就不多介绍了,直接看范例

下面的代码定义了一个函数 display ,它接受一个 Employee 结构体实例作为参数并输出结构体的所有元素

fn display( emp:Employee) {
   println!("Name is :{} company is {} age is 
   {}",emp.name,emp.company,emp.age);
}

完整的可运行的实例代码如下

//定义一个结构体
struct Employee {
   name:String,
   company:String,
   age:u32
}
fn main() {
   //初始化结构体
   let emp1 = Employee {
      company:String::from("TutorialsPoint"),
      name:String::from("Mohtashim"),
      age:50
   };
   let emp2 = Employee{
      company:String::from("TutorialsPoint"),
      name:String::from("Kannan"),
      age:32
   };
   //将结构体作为参数传递给 display
   display(emp1);
   display(emp2);
}

// 使用点号(.) 访问符访问结构体的元素并输出它么的值
fn display( emp:Employee){
   println!("Name is :{} company is {} age is 
   {}",emp.name,emp.company,emp.age);
}

编译运行以上 Rust 代码,输出结果如下

Name is :Mohtashim company is TutorialsPoint age is 50
Name is :Kannan company is TutorialsPoint age is 32

6. 结构体实例作为函数的返回值

Rust 中的结构体不仅仅可以作为函数的参数,还可以作为 函数的返回值

函数返回结构体实例需要实现两个地方:

  1. 箭头 -> 后面指定结构体作为一个返回参数。
  2. 在函数的内部返回 结构体的实例

结构体实例作为函数的返回值的语法格式:

struct My_struct {}

fn function_name([parameters]) -> My_struct {
   // 其它的函数逻辑
   return My_struct_instance;
}

下面的代码,我们首先定义一个结构体 Employee ,接着定义一个方法 who_is_elder ,传入两个结构体 Employee 作为参数并返回年龄个大的那个。

fn main() {

   let emp1 = Employee{
      company:String::from("TutorialsPoint"),
      name:String::from("Mohtashim"),
      age:50
   };
   let emp2 = Employee {
      company:String::from("TutorialsPoint"),
      name:String::from("Kannan"),
      age:32
   };
   let elder = who_is_elder(emp1,emp2);
   println!("elder is:");

   display(elder);
}

//接受两个 Employee 的实例作为参数并返回年长的那个
fn who_is_elder (emp1:Employee,emp2:Employee)->Employee {
   if emp1.age>emp2.age {
      return emp1;
   } else {
      return emp2;
   }
}

// 显示结构体的所有元素
fn display( emp:Employee) {
   println!("Name is :{} company is {} age is {}",emp.name,emp.company,emp.age);
}

// 定义一个结构体
struct Employee {
   name:String,
   company:String,
   age:u32
}

编译运行以上 Rust 代码,输出结果如下

elder is:
Name is :Mohtashim company is TutorialsPoint age is 50

7. 结构体中的方法

Rust 中的结构体可以定义 方法 ( method )

方法 ( method ) 是一段代码的 逻辑组合,用于完成某项特定的任务或实现某项特定的功能。

方法( method )函数( function) 有什么不同之处呢?

简单的说:

  1. 函数( function) 没有属主,也就是归属于谁,因此可以直接调用。
  2. 方法( method ) 是有属主的,调用的时候必须指定 属主
  3. 函数( function) 没有属主,同一个程序不可以出现两个相同签名的函数。
  4. 方法( method ) 有属主,不同的属主可以有着相同签名的方法。

定义方法时需要使用 fn 关键字。

fn 关键字是 function 取头尾两个字母的缩写。

结构体方法的 作用域 仅限于 结构体内部

与 C++ 语言 中的结构体的方法不同的是,Rust 中的结构体方法只能定义在结构体的外面。

在定义结构体方法时需要使用 impl 关键字,语法格式如下

struct My_struct {}

impl My_struct { 
   // 属于结构体的所有其它代码
}

impl 关键字最重要的作用,就是定义上面我们所说的 方法的属主。所有被 impl My_struct 块包含的代码,都只属于 My_struct 这个结构。

impl 关键字是 implement 的前 4 个字母的缩写。意思是 实现

结构体的普通方法(后面我们还会学到其它方法)时,第一个参数永远是 &self 关键字。self 是 自我 的意思,&self 永远表示着当前的结构体的一个实例。

这是不是可以带来其它的结构体方法的解释:结构体的方法就是用来操作当前结构体的一个实例的。

1)结构体方法的定义语法

定义结构体方法的语法格式如下

struct My_struct {}
impl My_struct { 

   // 定义一个结构体的普通方法
   fn method_name(&self[,other_parameters]) { 
      //方法的具体逻辑代码
   }

}

&self 是结构体普通方法固定的第一个参数,其它参数则是可选的。

即使结构体方法不需要传递任何参数,&self 也是固定的,必须存在的。像下面这种定义方法是错误的

struct My_struct {}
impl My_struct { 

   //定义一个结构体的普通方法
   fn method_name([other_parameters]) { 
      //方法的具体逻辑代码
   }
}

)结构体方法内部访问结构体元素

因为我们在定义方法时固定传递了 &self 关键字。而 &self 关键字又代表了当前方法的属主。

因此我们可以在方法内部使用 self. 来访问结构体的元素。

详细的语法格式如下

struct My_struct {
   age: u32
}

impl My_struct { 

   //定义一个结构体的普通方法
   fn method_name([other_parameters]) {

      self.age = 28;

      println!("{}",self.age);

      //其它的具体逻辑代码
   }
}

3)结构体方法的调用语法

因为结构体的方法是有属主的,所以调用的时候必须先指定 属主,调用格式为 属主.方法名(方法参数)。

详细的调用语法格式为

My_struct.method_name([other_parameters])

注意:虽然定义方法时需要固定 &self 作为第一个参数,但在调用的时候是 不需要也不能 传递的。这个参数的传递 Rust 编译器会 偷偷的 帮我们完成。

范例

下面的代码,我们首先定义了一个结构体 Rectangle 用于表示一个 长方形,它有宽高 两个元素 width 和 height。

然后我们又为 结构体 Rectangle 定义了一个方法 area 用于计算当前 长方形实例 的面积。

// 定义一个长方形结构体
struct Rectangle {
   width:u32, height:u32
}

// 为长方形结构体定义一个方法,用于计算当前长方形的面积
impl Rectangle {
   fn area(&self)->u32 {
      // 在方法内部,可以使用点号 `self.` 来访问当前结构体的元素。use the . operator to fetch the value of a field via the self keyword
      self.width * self.height
   }
}

fn main() {
   // 创建 Rectangle 结构体的一个实例
   let small = Rectangle {
      width:10,
      height:20
   };

   //计算并输出结构体的面积
   println!("width is {} height is {} area of Rectangle 
   is {}",small.width,small.height,small.area());
}

编译运行以上 Rust 代码,输出结果如下

width is 10 height is 20 area of Rectangle is 200

8. 结构体的静态方法

Rust 中的结构体还可以有静态方法。

静态方法可以直接通过结构体名调用而无需先实例化。

结构体的静态方法定义方式和普通方法类似,唯一的不同点是 不需要使用 &self 作为参数

1)定义静态方法的语法

结构体静态方法的定义语法格式如下

impl Structure_Name {

   // Structure_Name 结构体的静态方法
   fn method_name(param1: datatype, param2: datatype) -> return_type {
      // 方法内部逻辑
   }

}

静态方法和其它普通方法一样,参数是可选的。也就是可以没有参数

2)调用静态方法的语法

静态方法可以直接通过结构体名调用,而无需先实例化。

结构体的静态方法需要使用 structure_name:: 语法来访问,详细的语法格式如下

structure_name::method_name(v1,v2)

下面的范例,我们为结构体 Point 定义了一个静态方法 getInstance()。

getInstance() 是一个 工厂方法,它初始化并返回结构体 Point 的实例。

//声明结构体 Point
struct Point {
   x: i32,
   y: i32,
}

impl Point {
   // 用于创建 Point 实例的静态方法
   fn getInstance(x: i32, y: i32) -> Point {
      Point { x: x, y: y }
   }
   // 用于显示结构体元素的普通方法
   fn display(&self){
      println!("x ={} y={}",self.x,self.y );
   }
}
fn main(){

   // 调用静态方法
   let p1 = Point::getInstance(10,20);
   p1.display();

}

编译运行以上 Rust 代码,输出结果如下

x =10 y=20

下一章:Rust 模块

Rust 模块(mod):用于将函数或结构体按照功能分组。我们通常把相似的函数或者实现相同功能的或者共同实现一个功能的函数和结构体划分到一个模块中。Rust 中的模块,类似于其它语言中的模块或者包的概念,例如 C++ 语言中的命名空间 或者 Java 语言中的包。比模块更高级别的分组是 crate,我们可以将多个模块放到一个 crate下面。