6.结构体

一、普通结构体(struct)

1.1 说明

  • 需要为所有字段定义名称及类型
  • 结构体可以作为函数的返回值
  • 一旦struct的实例是可变的,那么实例中所有字段都是可变的
  • 实例化时,结构体字段的顺序不需要和它们在声明的的顺序一致

1.2 举例

1)结构体定义及访问

//定义结构体
struct User {
    username: String,
    email: String,
    sing_in_count: u64,
    active: bool,
}

//得到一个结构体实例(未按照定义时的顺序)
fn get_new_user() -> User {
    User {
        email: String::from("ch@163.com"),
        sing_in_count: 86,
        username: String::from("ch@163.com"),
        active: false,
    }
}

fn main() {

    //定义一个可变的结构体实例
    let mut user1 = User {
        email: String::from("xxx@163.com"),
        username: String::from("xxx"),
        active: true,
        sing_in_count: 556,
    };

    let user2 = get_new_user();

    println!("user1: email = {}", user1.email); //xxx@163.com

    user1.email = String::from("yyy@189.com");  //修改可变的值
    println!("user1: email  = {}", user1.email);  //yyy@189.com
    println!("user2: email  = {}", user2.email);  //ch@163.com
}

2)结构体初化的简单写法

  • 前面得到实例时都要重复写字段名称和变量,过于繁琐;
  • 当字段名与字段值对应变量名相同时,就可以使用字段初始化简写的方式;
fn get_new_user(email: String, username: String) -> User {
    User {
        email,
        sing_in_count: 86,
        username,
        active: false,
    }
}

3)结构体更新语法

  • 通过改变旧实例中少部分值来创建一个新的结构体实例可以使用结构体更新语法
  • .. 旧实例语法指定了剩余未显式设置值的字段应有与给定实例对应字段相同的值;
    *..旧实例必须放在最后,以指定其余的字段应从旧实例的相应字段中获取其值,但我们可以选择以任何顺序为任意字段指定值,而不用考虑结构体定义中字段的顺序;
  • ..旧实例后不能加逗号;
  • 要注意所有权转移的问题
struct User {
    username: String,
    email: String,
    sing_in_count: u64,
    active: bool,
}

fn get_new_user() -> User {
    let username = String::from("ch@163.com");
    let email = String::from("ch@163.com");
    User {
        username,
        email,
        sing_in_count: 86,
        active: false,
    }
}

fn main() {
    let user1 = get_new_user();

    //未采用更新语法
    let user2 = User{
        username: String::from("user2"),
        email: String::from("user2@163.com"),
        sing_in_count: user1.sing_in_count,
        active: user1.active,
    };

    //采用结构体更新语法
    let user3 = User{
        username: String::from("user3"),
        ..user1  //不能写逗号,其它的用user1的赋值
    };
    
    // println!("user1.username = {}", user1.email); //所有权已经转移到user3,所以这里不能使用

    println!("user2.username = {}, sing_in_count = {}", user2.username, user2.sing_in_count);
    println!("user3.username = {}, sing_in_count = {}", user3.username, user3.sing_in_count);
}

二、元组结构体(tuple struct)

2.1 概念

  • 定义与元组类似的结构体,称为元组结构体
  • 元组结构体有着结构体名称提供的含义,但没有具体的字段名,只有字段的类型
  • 当想给整个元组取一个名字,并使元组成为与其他元组不同的类型时,可以使用元组结构体;
  • 要定义元组结构体,以 struct 关键字和结构体名开头并后跟元组中的类型;

2.2 示例

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

    let black = Color(3, 4, 5);
    let origin = Point(33, 15, 14);

    println!("{} {} {}", black.0, black.1, black.2);
    println!("{} {} {}", origin.0, origin.1, origin.2);
}

三、类单元结构体(unit-like structs)

3.1 概念

  • 类单元结构体是一个没有任何字段的结构体
  • 类似于 (),即单元类型(只有一个值,被称为单元值);
  • 类单元结构体常常在想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用;

3.2 举例

struct AlwaysEqual;

fn main() {
    let subject = AlwaysEqual;
}
  • 使用struct关键字定义 AlwaysEqual,以分号结尾;
  • 在main函数中,以代码所示的方法获得 AlwaysEqual 的实例变量 subject;
  • 下一步将实现这个类型的行为,即每个实例始终等于每一个其他类型的实例,也许是为了获得一个已知的结果以便进行测试。现在不需要任何数据来实现这种行为。在以后的文章里将会看到如何定义特性并在任何类型上实现它们,包括类单元结构体。

四、类的使用示例

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

//只传递引用,因此不会获取传入变量的所有权
fn area(rect: &Rectangle) -> u32 {
    rect.width * rect.height
}

fn main() {
    let rc = Rectangle{
        width: 53,
        height: 20,
    };

    println!("area = {}", area(&rc));
    println!("rc = {:#?}", rc)
}
  • 代码计算长方体的面积;
  • area传入的是引用,因此不会获得变量的所有权;
  • #[derive(Debug)]与println的{:#?}配合使用是为了查看实体化后rc的值,如果把前者取消并且使用println!("rc={}",rc)打印结果,会得到如下错误
    在这里插入图片描述
    按照说明修改println!的参数再编译会得到如下错误
    在这里插入图片描述
    因此,根据提示一步步修改就会得到正确的结果

五、为结构体添加实现方法

5.1 概念

  • 使用fn关键字和名称声明,可以拥有参数和返回值;
  • 方法在结构体的上下文中被定义(或者是枚举或 trait 对象的上下文),并且第一个参数总是 self,代表调用该方法的结构体实例;
  • 一般将方法的第一个参数写为&self,只希望能够读取结构体中的数据,而不是写入;
  • 想要在方法中改变调用方法的实例,需要将第一个参数改为&mut self
  • 通过仅仅使用 self 作为第一个参数来使方法获取实例的所有权是很少用的;该功能通常用在当方法将 self 转换成别的实例的时候,想要防止调用者在转换之后使用原始的实例。

5.2 举例

  • area方法实现了面积的计算;
  • can_hold方法计算当前对象是否能够包围住第二个Rectangle对象的实例;
#[derive(Debug)]
struct Rectangle{
    width: u32,
    height: u32,
}

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

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

fn main() {
    let rc = Rectangle{
        width: 53,
        height: 20,
    };

    let rc2 = Rectangle{
        width: 51,
        height: 20,
    };

    println!("{}", rc.can_held(&rc2));
}

六、关联函数

6.1 概念

  • 所有在 impl 块中定义的函数被称为关联函数;
  • 可以定义不以 self为第一参数的关联函数(类似于类的静态函数),String::from就是一个关联函数;

6.2 示例

使用关联函数创建正文形并计算面积。

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

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

    fn square(size: u32) -> Rectangle{
        Rectangle{
            width: size,
            height: size,
        }
    }
}

fn main() {
    let rc = Rectangle::square(3);
    println!("rc.area = {}", rc.area());
}
  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C语言结构常见错误包括以下几种情况: 1. 未声明结构变量:在使用结构之前,需要先声明结构变量。如果没有声明结构变量就直接使用,会导致编译错误。 2. 结构成员命名冲突:在同一个结构中,不能有相同名称的成员变量。如果出现了命名冲突,会导致编译错误。 3. 结构成员类型错误:结构成员的类型必须是合法的C语言类型,不能是其他结构类型。如果使用了非法的类型,会导致编译错误。 4. 结构成员访问错误:在访问结构成员时,需要使用成员运算符“.”。如果使用了其他的运算符或者语法错误,会导致编译错误。 5. 结构大小计算错误:在计算结构大小时,需要考虑对齐和填充的规则。如果没有正确计算结构大小,可能会导致内存访问错误或者数据错乱。 6. 结构赋值错误:在给结构赋值时,需要逐个成员进行赋值。如果使用了错误的赋值方式,可能会导致数据错误或者编译错误。 7. 结构传参错误:在函数参数中传递结构时,需要使用正确的传参方式。如果使用了错误的传参方式,可能会导致函数调用错误或者数据错误。 8. 结构嵌套错误:在定义嵌套结构时,需要注意结构的顺序和嵌套关系。如果嵌套关系错误,可能会导致数据错误或者编译错误。 9. 结构成员顺序错误:在定义结构成员时,需要按照正确的顺序进行定义。如果顺序错误,可能会导致数据错误或者编译错误。 10. 结构成员未初始化:在定义结构变量时,需要对结构成员进行初始化。如果没有正确初始化结构成员,可能会导致数据错误或者编译错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贱贱的剑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值