rust的虚类型参数

一、虚类型参数

有过编程经验的同学可能知道,在一些程序的函数参数里有哑元一说,在c++的一些std::function这个函数类型中,也有占位符一说。这也就是说,在实际的编程中,可能会遇到一些参数必须有,但又没有实际意义,而又不能占用空间。这其实如果最简单的处理方法是直接告诉编译器配置一下,但一些代码不可能在一个平台,一个编译器下使用,这就使得复杂性无形变得增加不少。
在Rust中,为了处理这种情况,使用了虚类型参数或者有的叫做幽灵数据,虚类型(phantom type)参数和上面描述的一致,是需要编译器做静态类型检查的参数,它在实际运行中是不可见的或者说没用任何作用。它只是充当一个标记(类似于哑元或者点位符),它不占用任何空间,不存储任何数据,只是提供一个额外的泛型类型参数指定数据类型供编译时类型检查。
在Rust的文档中这样描述:
When working with unsafe code, we can often end up in a situation where types or lifetimes are logically associated with a struct, but not actually part of a field. This most commonly occurs with lifetimes.

二、应用场景

在Rust中,可以使用std::marker::PhantomData来实现虚类型的应用,它主要的应用场景有以下几种:
1、生命周期内未使用的参数(Unused lifetime parameters,Unused type parameters)
下面这种是编译无法通过的:

struct Slice<'a, T> {
    start: *const T,
    end: *const T,
}

需要修改为:

use std::marker::PhantomData;

struct Slice<'a, T: 'a> {
    start: *const T,
    end: *const T,
    phantom: PhantomData<&'a T>,
}

2、标记拥有关系(Ownership and the drop check)


fn main() {
struct Vec<T> {
    data: *const T, // *const for variance!
    len: usize,
    cap: usize,
}
}

需要修改为:

fn main() {
use std::marker;

struct Vec<T> {
    data: *const T, // *const for variance!
    len: usize,
    cap: usize,
    _marker: marker::PhantomData<T>,
}
}

3、自动Trait的实现(Trait Implementations)
看一个官网上的例程:

pub struct PhantomData<T: ?Sized>;

impls! { PhantomData }

mod impls {
    #[stable(feature = "rust1", since = "1.0.0")]
    unsafe impl<T: Sync + ?Sized> Send for &T {}
    #[stable(feature = "rust1", since = "1.0.0")]
    unsafe impl<T: Send + ?Sized> Send for &mut T {}
}

4、型变(协变和逆变)
这个玩意儿估计搞过一些高级语言的都知道是干啥的,这里就不再展开了,有兴趣的可以查查资料。

在Rust中有一张表总结PhantomData应用场景:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lzOJn6BI-1638774392147)(img/phantom.png)]

三、例程

看一个综合的例子:

use std::marker::PhantomData;

// 这个虚元组结构体对 `A` 是泛型的,并且带有隐藏参数 `B`。
#[derive(PartialEq)] // 允许这种类型进行相等测试(equality test)。
struct PhantomTuple<A, B>(A,PhantomData<B>);

// 这个虚类型结构体对 `A` 是泛型的,并且带有隐藏参数 `B`。
#[derive(PartialEq)] // 允许这种类型进行相等测试。
struct PhantomStruct<A, B> { first: A, phantom: PhantomData<B> }

// 注意:对于泛型 `A` 会分配存储空间,但 `B` 不会。
//       因此,`B` 不能参与运算。

fn main() {
    // 这里的 `f32` 和 `f64` 是隐藏参数。
    // 被指定为 `<char, f32>` 的 `PhantomTuple` 类型。
    let _tuple1: PhantomTuple<char, f32> = PhantomTuple('Q', PhantomData);
    // 被指定为 `<char, f64>` `PhantomTuple` 类型。
    let _tuple2: PhantomTuple<char, f64> = PhantomTuple('Q', PhantomData);

    // 被指定为 `<char, f32>` 的类型。
    let _struct1: PhantomStruct<char, f32> = PhantomStruct {
        first: 'Q',
        phantom: PhantomData,
    };
    // 被指定为 `<char, f64>` 的类型。
    let _struct2: PhantomStruct<char, f64> = PhantomStruct {
        first: 'Q',
        phantom: PhantomData,
    };
    
    // 编译期错误!类型不匹配,所以这些值不能够比较:
    //println!("_tuple1 == _tuple2 yields: {}",
    //          _tuple1 == _tuple2);
    
    // 编译期错误!类型不匹配,所以这些值不能够比较:
    //println!("_struct1 == _struct2 yields: {}",
    //          _struct1 == _struct2);
}

  • 这个例子也是出息社区文档。

四、总结

老生常谈,不管一门新技术还是一门新语言等等,新东西完全是新的可能性不大,大多是要在原来的基础上进行完善或者重新建立但有着原来的影子。Rust这个幽灵数据正是如此。
努力吧,归来的少年!
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值