暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Rust 简易笔记(4)—— Struct结构体

沛沛聊AI 2021-09-04
1085

本篇是Rust简易笔记的第四篇

     感谢大家关注!

▼▼▼

关于Rust的前景、优势等不再赘述,这篇主要讲Rust的Struct特性,与C++中的Class有相似性



本系列将以“笔记”作为撰文形式,追求简洁明了,概念随时配合代码。如果行文过程中没有讲清楚的地方,欢迎后台私信。如有需要(请后台告诉我),Rust笔记系列更新完成后会再写一份详细的Rust图文教程。





01

总览

与tuple的区别是:structs不需要依赖于数据类型中的顺序来access,而是通过键值对。





02

定义struct和构造实例

struct User {
   username: String,
   email: String,
   sign_in_count: u64,
   active: bool,
}

fn structs_test() {
   let mut user1 = User {
       email: String::from("someone@example.com"),
       username: String::from("some_username"),
       active: true,
       sign_in_count: 1,
  };
   user1.email = String::from("another@example.com"); // 获取struct中某个filed可以用点符号。
}

在struct里,我们把数据名和它的类型称为filed。每个filed都是一组键值对。如果要对struct变量改值,需要将整个struct变量设为mut,而不能只对某个filed设为mut。注意:struct类型在同一namespace中只能定义一次。

如果某函数参数的变量名与struct的filed name一样,那么就不用再写filed name了,即:

fn build_user(email: String, username: String) -> User {
   User {
       email,
       username,
       active: false,
       sign_in_count: 2,
  }
}

// 在调用函数中
fn structs_test() {
   let user2 = build_user(String::from("test@example.com"), String::from("test"));
   println!("email: {}, username: {}, sing_in_count: {}, activate: {}",
            user2.email, user2.username, user2.sign_in_count, user2.active);
   // 输出:email: test@example.com, username: test, sing_in_count: 2, activate: false
}

除了对struct直接赋值之外,我们还可以使用struct更新的语法。使用..user1
的方法给新的struct变量把剩余未指明的field内容补充为与user1
相同的值。其中user1
是一个struct变量。如下:

fn update_struct(user1: User, email: String, username: String) -> User {
   User {
       email,
       username,
      ..user1
  }
}

// 在调用函数中
fn structs_test() {
   let user3 = update_struct(user1, String::from("update@example.com"), String::from("update"));
   println!("email: {}, username: {}, sing_in_count: {}, activate: {}",
            user3.email, user3.username, user3.sign_in_count, user3.active);
   // 输出:email: update@example.com, username: update, sing_in_count: 1, activate: true
}

还可以使用tuple structs,即长得像tuple的structs。其用法与tuple大致相似:都可以通过点符号access,但是不能把tuple structs赋值给tuple。另外,哪怕两个struct的组成元素的类型和值完全相同,这也是两个不同的struct。如下:

fn tuple_structs() {
   struct Color(i32, i32, i32);
   struct Point(i32, i32, i32);

   let black = Color(1, 2, 3);
   let origin = Point(1, 2, 3);

   // 只能传入一个struct Color的变量
   fn test_tuple_structs(tuple_structs: Color) {
       // let (x, y, z) = tuple_structs; error: expected struct `Color`, found tuple
       println!("The second value in tuple structs is: {}", tuple_structs.1);
       // 输出:The second value in tuple structs is: 2
  }

   test_tuple_structs(black);
   // test_tuple_structs(origin); error: expected struct `Color`, found struct `Point`
}

最后,我们还可以声明一个没有filed的struct,称为unit-like structs:

struct UnitStruct {}
let unit_struct = UnitStruct {};

上述例子中的string全是用了自有ownership的String,这也就保证了整个struct的lifetimes与每个filed的lifetimes保持一致。当然,任何一个filed可以使用引用reference,其ownership被其他struct外部的变量持有。但是这时要求指定其lifetimes,否则就会报错:

struct UserRef {
   username: &str, // error: expected named lifetime parameter
   sign_in_count: u64,
   active: bool,
}





03

Structs的打印

rust对struct的打印函数进行了默认支持,但是我们需要告诉编译器我们想要什么样的打印方式。

首先在struct定义时添加#[derive(Debug)]
标记,明确使用打印调试信息的功能。

之后在println!
函数的format中指明打印格式,如{:#?}
或者{:?}

// 添加这个标记明确使用打印调试信息的功能
#[derive(Debug)]
struct Rectangle {
   width: u32,
   height: u32,
}

// 在调用函数中
fn structs_test() {
   let rect1 = Rectangle {
       width: 30,
       height: 50,
  };
   println!("rect1 is {:#?}", rect1);
   println!("rect1 is {:?} in another format", rect1);
}

输出如下:

rect1 is Rectangle {
  width: 30,
  height: 50,
}
rect1 is Rectangle { width: 30, height: 50 } in another format





04

Structs的方法(Method)

struct可以通过impl关键字定义method,类似c++里class的成员函数。这里的self类似c++里class的this指针。

如果method的参数不止self,新加的参数需要添加到self之后。

如果参数列表中没有self,这种函数称为associated function而不是method。比如之前经常出现的String::from()
函数。这种函数的好处是不需要某个具体的Rectangle实例就可以调用。

#[derive(Debug)]
struct Rectangle {
   width: u32,
   height: u32,
}

impl Rectangle {
   // 如果要在函数中改变参数的值,入参改为&mut self
   // 如果需要把self转为其他内容,并希望阻止其调用函数保留原值,这时才改为去掉引用的self
   fn area(&self) -> u32 {
       self.width * self.height
  }

   // 其他参数必须加到self之后
   fn can_hold(&self, other: &Rectangle) -> bool {
       self.width > other.width && self.height > other.height
  }

   // 参数列表中可以没有self,这种称为associated function,不是method。通常用来构造一个新的实例。
   fn square(size: u32) -> Rectangle {
       Rectangle {
           width: size,
           height: size,
      }
  }
}

// 在调用函数中
fn structs_test() {
   let rect1 = Rectangle {
       width: 30,
       height: 50,
  };
   println!("The area of the rectangle is {}", rect1.area());

   let rect2 = Rectangle {
       width: 40,
       height: 60,
  };
   let rect3 = Rectangle {
       width: 10,
       height: 30,
  };
   println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
   println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));

   let sq = Rectangle::square(30);
   println!("sq is {:#?}", sq);
}

输出为:

The area of the rectangle is 1500
Can rect1 hold rect2? false
Can rect1 hold rect3? true
sq is Rectangle {
  width: 30,
  height: 30,
}

这些method可以处于不同的impl里面(一般涉及generic types和traits时会这样写),比如:

impl Rectangle {
   fn area(&self) -> u32 {
       self.width * self.height
  }
}

impl Rectangle {
   fn can_hold(&self, other: &Rectangle) -> bool {
       self.width > other.width && self.height > other.height
  }
}



以上是Rust的Struct特性,配套代码见:

    https://github.com/ds1231h/RustSimpleCode 


    src/structs.rs


    Rust的安装、编译与运行参加本系列第一篇:

    Rust 简易笔记(1)—— 安装与配置







    想要了解最新鲜技术科普吗?
    想要学习
    最硬核的技术干货吗?

    点击下方链接
    关!注!公!众!号!

    了解更多有趣的硬核干货
    ▼▼▼

    撰文:沛沛


    文章转载自沛沛聊AI,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论