专栏首页Rust语言学习交流【Rust每周一知】理解智能指针Box<T>

【Rust每周一知】理解智能指针Box<T>

主要内容包括:

  1. 基础概念:指针,引用,智能指针
  2. 智能指针Box<T>DerefDrop

1. 基础概念

1.1 指针

指针是个通用概念,它表示内存地址这种类型,其引用或“指向”其他数据。Rust中的指针是“第一类公民”(first-class values),可以将它们移动或复制,存储到数据结构中并从函数中返回。Rust提供了多种类型的指针:

  • 引用(Reference),共享引用&,不可变引用&mut
  • 原生指针(Raw Pointer),*const*mut
  • 智能指针(Smart Pointer),Box<T>Rc<T>

1.2 引用

Rust中使用&符号表示引用,也叫引用操作符。其使用场景是只使用类型的值但不获取其所有权,同时Rust的引用规则为:

  • 在作用域中的数据有且只能有一个可变引用;
  • 可以有多个不可变引用;
  • 不能同时拥有不可变引用和可变引用。

注:一个引用的作用域从声明的地方开始一直持续到最后一次使用为止。

参见以下示例:

fn main() {
let arr = [1,2,3];
let addr = &arr; // 通过 & 得到引用,默认是不可变的
println!("{:p}", addr); // 内存地址
	
let mut vec = vec![1,2,3]; // 要获取可变引用,必须先声明可变绑定
let new_vec = &mut vec; // 通过 &mut 得到可变引用
	new_vec.push(4);
println!("{:?}", new_vec); // [1, 2, 3, 4]	
let mut str1 = String::from("hello");
let m1 = &mut str1;
let m2 = &mut str1; // ERROR:只能有一个可变引用
println!("{}, {}", m1, m2);
println!("{}", m2); // WORK:m1 作用域结束
	
let mut str2 = String::from("world");
let r1 = &str2;
let r2 = &str2; // 没问题
let r3 = &mut str2;
println!("{}, {}, and {}", r1, r2, r3); // ERROR:不能同时拥有不可变引用和可变引用
println!("{}", r3); // WORK:r1 和 r2 作用域结束
}

从语义上说,不管是&还是&mut,都是对原有变量的所有权的借用,所以引用也被称为借用。

1.3 智能指针

智能指针的概念起源于C++,智能指针是一类数据结构,他们的表现类似指针,但是拥有额外的元数据和功能。

在Rust中,引用和智能指针的一个的区别是引用是一类只借用数据的指针;相反,在大部分情况下,智能指针拥有他们指向的数据。Rust标准库中不同的智能指针提供了比引用更丰富的功能:

  • Box<T>,用于在堆上分配数据。
  • Rc<T>,一个引用计数类型,其数据可以有多个所有者。
  • Ref<T>RefMut<T>,通过RefCell<T>访问,一个在运行时而不是在编译时执行借用规则的类型。

2. 智能指针Box<T>

在Rust中,所有值默认都是栈上分配。通过创建Box<T>,可以把值装箱,使它在堆上分配。Box<T>类型是一个智能指针,因为它实现了Dereftrait,它允许Box<T>值被当作引用对待。当Box<T>值离开作用域时,由于它实现了Droptrait,首先删除其指向的堆数据,然后删除自身。

2.1 Deref

Deref这个trait,允许我们重载解引用运算符*。实现Deref的智能指针可以被当作引用来对待,也就是说可以对智能指针使用*运算符进行解引用。

Box<T>Deref的实现:

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Deref for Box<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &**self
    }
}

该实现返回&**self。为什么呢?由于self是一个&Box<T>,因此对其进行一次解引用*将获得一个Box<T>,而第二次解引用*将获得一个T。最后,将其包装在引用&中并返回。

:如果是我们自定义的类型,要实现deref,则不能仿照它,否则会造成无限递归。

2.2 Drop

Drop这个trait的主要作用是释放实现者实例拥有的资源。它只有一个方法:drop,当实例离开作用域时会自动调用该方法,从而调用实现者指定的代码。

Box<T>Drop的实现:

#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<#[may_dangle] T: ?Sized> Drop for Box<T> {
    fn drop(&mut self) {
        // FIXME: Do nothing, drop is currently performed by compiler.
    }
}

2.3 Box<T>

Box<T>是堆上分配的指针类型,称为“装箱”(boxed),其指针本身在栈,指向的数据在堆,在Rust中提供了最简单的堆分配类型。使用Box<T>的情况:

  • 递归类型和trait对象。Rust需要在编译时知道一个类型占用多少空间,Box<T>的大小是已知的。
  • “大”的数据转移所有权。用Box<T>只需拷贝指针。

递归类型的经典示例:

use List::{Cons, Nil};

#[derive(Debug)]
enum List<T> {
    Cons(T, Box<List<T>>),
    Nil,
}

fn main() {
let recursive_list: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Nil))));
println!("{:?}", recursive_list); // 打印出:Cons(1, Cons(2, Nil))
}

trait对象的示例:

trait T {
    fn m(&self) -> u64;
}
  
struct S {
    i: u64
}
  
impl T for S {
    fn m(&self) -> u64 { self.i }
}

fn f(x: Box<dyn T>) {
    println!("{}", x.m())
}
  
fn main() {
    let s = S{i : 100};
    println!("{}", s.m());

    let b: Box<S> = Box::new(S{i: 100});
    f(b);
}

本文示例代码:https://github.com/lesterli/rust-practice/tree/master/head-first/std-box

本文分享自微信公众号 - Rust语言学习交流(rust-china)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-02-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【翻译】200行代码讲透RUST FUTURES (2)

    在我们深入研究 Futures in Rust 的细节之前,让我们快速了解一下处理并发编程的各种方法,以及每种方法的优缺点。

    MikeLoveRust
  • 【翻译】200行代码讲透RUST FUTURES (5)

    生成器的动机可以在 RFC#2033中找到。它写得非常好,我建议您通读它(它谈论async/await的内容和谈论生成器的内容一样多)。

    MikeLoveRust
  • 硬核教程 - 使用Rust编写网游FPS外挂辅助

    优势:Rust没有GC 效率和C++一样快 本贴子主要用于观摩和学习调用windows api,禁止用于某些用途

    MikeLoveRust
  • Content Provider 之 最终弹 实战体验跨程序数据共享(结合SQLiteDemo)

    本模块共有四篇文章,参考郭神的《第一行代码》,对Content Provider的学习做一个详细的笔记,大家可以一起交流一下:

    凌川江雪
  • 龙珠直播公布全新战略,会借力苏宁再上台阶吗?

    2017年直播行业迎来一轮大洗牌,不少独立创业型直播平台都进入了巨头怀抱,但这并不意味着直播之战的结束,在巨头撑腰之后反而更加激烈,玩法也得到了升级。6月27日...

    罗超频道
  • 再造一个轮,又一个测试云平台

    市面上有很多的测试工具,比如测试接口有Jmeter、Postman等,自动化有Robotframework、Appium、Selenium等,每个人掌握的技能不...

    归根落叶
  • 详解Retrofit Interceptor(拦截器) 拦截请求并做相关处理

    本文介绍Retrofit拦截器(Interceptor)的使用方法及相关注意事项。如果本文对您有所帮助,烦请点亮小红心~

    砸漏
  • Kotlin实用的语法糖:空安全、类型转换 & 相等性判断

    Kotlin被Google官方认为是Android开发的一级编程语言。今天,我将主要讲解,关于Kotlin的一些实用语法糖,主要包括:

    Carson.Ho
  • Now 直播应用的后台服务器性能测试实践

    直播的火爆带来了海量的用户,也带来了海量的服务器并发。本文分析了目前直播行业存在的难点,从腾讯目前的新直播产品的 NOW 直播出发, 了解直播应用背后的那些事...

    WeTest质量开放平台团队
  • 干货 | 小程序直播解决方案场景操作指南

    小程序直播解决方案发布以来,受到越来越多的客户关注。同时,因小程序的类目、准入政策、插件集成等原因,在产品使用选择上有点小困惑~ 小编结合大家最关心的问题,来...

    腾讯云视频

扫码关注云+社区

领取腾讯云代金券