Rust 基础知识7 - Struct 相关知识

简介

  • 接上回书

什么是struct

  • struct,是结构体,是一个自定义的数据类型,目的是关联相关不同变量。

定义struct

  • 使用struct 关键字,并为整个struct命名。
  • 在花括号内,为所有字段定义名称和类型。
  • 使用struct 需要创建 struct 的实例
1、为每个字段制定具体值。
2、无需按声明顺序制定。
  • 获取struct 的值,通过点标记法即可 struct.filed_name
  • 特别需要注意如果 struct 声明为可变的那么实例中的所有字段都是可变的。
  • 举例:
fn main() {
    struct Employee {
        username: String,
        age: i16,
        email: String,
    }

    let emp = Employee {
        username: String::from("LinHai."),
        age: 39i16,
        email: String::from("kami@cancanyou.com"),
    };

    println!("Winer is : {} {} {}", emp.username, emp.age, emp.email);
}

image.png
  • 字段初始化简写,当字段名与字段值的变量名称相同时可以使用简写方式,例如:(注意username、age变量的给值方法)
fn main() {
    struct Employee {
        username: String,
        age: i16,
        email: String,
    }

    let username = String::from("linhai");
    let age:i16 = 39;

    let emp = Employee {
        username, // 如果变量名称与结构定义的变量名相同则可以直接赋值
        age, // 如果变量名称与结构定义的变量名相同则可以直接赋值
        email: String::from("kami@cancanyou.com"),
    };

    println!("Winer is : {} {} {}", emp.username, emp.age, emp.email);
}

  • 还有一种比较特殊的 struct 的更新语法,可以基于某个已有的结构实例初始化新的对象,如下例子中注意 ..emp 的用法:
fn main() {
    struct Employee {
        username: String,
        age: i16,
        email: String,
    }

    let username = String::from("linhai");
    let age:i16 = 39;

    // 定义基础实例对象
    let emp = Employee {
        username, // 如果变量名称与结构定义的变量名相同则可以直接赋值
        age, // 如果变量名称与结构定义的变量名相同则可以直接赋值
        email: String::from("kami@cancanyou.com"),
    };

    let mut emp_child = Employee {
        username: String::from("林巍"),
        ..emp // 这种拓展语法可以基于某个结构建立结构
    };
    
    // 修改年龄
    emp_child.age = 3;

    println!("Winer is : {} {} {}", emp_child.username, emp_child.age, emp_child.email);
}

image.png

Tuple struct

  • 这是一个简化版的 struct,具体形式大致如下:
struct Color(i32,i32,i32);
struct Point(i32,i32);
let black = Color(0,0,0);
let origin = Point(0,0,0);
  • 还有一种更特殊的叫做 Unit-Like Struct 比如 struct Man {};

数据的所有权

  • 你会发现如果定一个变量,是String类型,但是随着赋值所有权会转移到结构中,之后在使用就会出错,比如:


    image.png
image.png
  • 具体的解决需要引入声明周期的概念,稍后再讲。

一个具体的实例

  • 接下来将根据这个程序进行改写。
fn main() {
    // 初始化结构
    let rec = Rectangle {
        width: 30,
        height: 50,
    };

    let result = area(&rec);
    println!("矩形:{:?},的面积是:{}", rec, result);
}

// 定一个个矩形结构
#[derive(Debug)]
struct Rectangle {
    width: i32,
    height: i32,
}

// 定一个区域函数
fn area(rectangle:&Rectangle) -> i32{
    rectangle.width * rectangle.height
}

Struct 的方法

  • 方法和函数类似,通过fn关键字定义,名称、参数和返回值。
  • 方法与函数不同的地方:
1、方法是在struct(enum、trait对象)的上下文中定义的。
2、方法的第一个参数是self,self指向被调用的struct 实例。
3、方法在 impl (implement)块中定义的` impl StructName {}`
4、方法的第一个参数可以使 &self,也可以获得其所有权或可变借用,和其他参数一样。
  • 改进上面的实例,主要是把 area 方法整合到 struct 中,这样整体结构更好:

fn main() {
    // 初始化结构
    let rec = Rectangle {
        width: 30,
        height: 50,
    };

    let result = rec.area(); // 改变了计算形式。
    println!("矩形:{:?},的面积是:{}", rec, result);
}

// 定一个个矩形结构
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}
// 这个就是给Struct 加方法的方式
impl Rectangle {
    // 定一个区域函数(从外面挪到块里面)
    fn area(&self) -> u32{
        self.width * self.height
    }
}

image.png
  • 方法调用的是的特别说明
/*
C/C++中: object->something() 等于 (*object).something() 也就是解构指针,Rust并不需要这样,Rust会自动引用或解引用。
在调用方法时,Rust根据情况自动添加 &、&mut 或 * ,以便 object 可以匹配方法的签名。
*/
p1.distance(&p2);
// 等于
(&p1).distance(&p2);

  • Struct 后面还可以跟随多个不同参数:
fn main() {
    // 初始化结构
    let rec = Rectangle {
        width: 30,
        height: 50,
    };

    let rec2 = Rectangle {
        width: 20,
        height: 30,
    };

    let rec3 = Rectangle {
        width: 70,
        height: 30,
    };

    let result = rec.area();
    println!("矩形:{:?},的面积是:{}", rec, result);

    println!("rec 能放下 rec2 ? {}", rec.can_hold(&rec2) );
    println!("rec 能放下 rec3 ? {}", rec.can_hold(&rec3) );
}

// 定一个个矩形结构
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // 定一个区域函数
    fn area(&self) -> u32{
        self.width * self.height
    }

    fn can_hold(&self, rec: &Rectangle) -> bool {
        self.width > rec.width && self.height > rec.height 
    }
}
image.png

关联函数(类似于静态方法)

  • 可以在impl 块中定义一个不以 self 作为第一个参数的函数,这种形式叫做关联函数,调用方式类似 String::from()
  • 关联函数通常用于构造器,举例:
fn main() {
    // 初始化结构
    let width = 60u32;
    let square = Rectangle::create_square(width);

    let result = square.area();
    println!("矩形:{:?},的面积是:{}", square, result);
    println!("开始定义的变量 width = {} 的所有权被交回来了因此并不会消失", width);
}

// 定一个个矩形结构
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // 定一个区域函数
    fn area(&self) -> u32{
        self.width * self.height
    }

    // 创建一个正方形,注意这里的参数 width 传递的是所有权
    fn create_square(width: u32) -> Rectangle {
        // 初始化后实际上把所有权返回了。
        Rectangle {
            width,
            height: width,        
        }
    }
}
image.png

Struct 的智能指针 Box<T>

1、智能指针的概念起源于C++,智能指针是一类数据结构,他们的表现类似指针,但是拥有额外的元数据和功能。
2、在Rust中,引用和智能指针的一个的区别是引用是一类只借用数据的指针;相反,在大部分情况下,智能指针拥有他们指向的数据。Rust标准库中不同的智能指针提供了比引用更丰富的功能:
------------------
Box<T>,用于在堆上分配数据。
Rc<T>,一个引用计数类型,其数据可以有多个所有者。
Ref<T> 和 RefMut<T>,通过RefCell<T>访问,一个在运行时而不是在编译时执行借用规则的类型。
  • 智能指针Box<T>
1、在Rust中,所有值默认都是栈上分配。
2、通过创建Box<T>,可以把值装箱,使它在堆上分配。
3、Box<T>类型是一个智能指针,因为它实现了Dereftrait,它允许Box<T>值被当作引用对待。
4、当Box<T>值离开作用域时,由于它实现了Droptrait,首先删除其指向的堆数据,然后删除自身。
  • Box<T> 的举例,如果一个类型指向他自己就可以通过Box<T>来实现,比如一个树的节点表示
#[derive(Debug)]
struct TreeNode {
    data :i32, 
    left_node : Box<TreeNode>,
    right_node : Box<TreeNode>,
}

结束

  • 感谢阅读。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容