[][src]Function tao_of_rust::ch13::security_abstract::variances

pub fn variances()

子类型和型变(variance)

PhantomData规则:

PhantomData,在T上是协变。 PhantomData<&'a T>,在'a 和T上是协变。 PhantomData<&'a mut T>,在'a上是协变,在T上是不变。 PhantomData<*const T>,在T上是协变。 PhantomData<*mut T>,在T上不变。 PhantomData<fn(T)>,在T上是逆变,如果以后语法修改的话,会成为不变。 PhantomData<fn() -> T>,在T上是协变。 PhantomData<fn(T) -> T>,在T上是不变。 PhantomData<Cell<&'a ()>>,在'a上是不变。

请配合书本中解释理解

Basic usage: 未合理使用型变将会引起未定义行为

虽然可以正常工作,但这里存在未定义行为的风险 原因是协变改变了生命周期,「忽悠」了借用检查

// 协变类型
struct MyCell<T> {
    value: T,
}
impl<T: Copy> MyCell<T> {
    fn new(x: T) -> MyCell<T> {
        MyCell { value: x }
    }
    fn get(&self) -> T {
        self.value
   }
   fn set(&self, value: T) {
       use std::ptr;
       unsafe {
           ptr::write(&self.value as *const _ as *mut _, value);
       }
   }
}

fn step1<'a>(r_c1: &MyCell<&'a i32>) {
    let val: i32 = 13;
    step2(&val, r_c1); // step2函数执行完再回到step1
    println!("step1 value: {}", r_c1.value);
} // step1调用完,栈帧将被清理,val将不复存在,&val将成为悬垂指针

fn step2<'b>(r_val: &'b i32, r_c2: &MyCell<&'b i32>) {
    r_c2.set(r_val);
}
static X: i32 = 10;
fn main() {
   let cell = MyCell::new(&X);
   step1(&cell);
   println!("  end value: {}", cell.value); //此处 cell.value的值将无法预期,UB风险
}Run

Basic usage: 修改MyCell 类型为不变

解决上面示例UB的问题,编译将报错,因为安全检查生效了,成功阻止了UB风险

use std::marker::PhantomData;
struct MyCell<T> {
    value: T,
    mark: PhantomData<fn(T)> , //通过PhantomData<fn(T)>将MyCell<T>改为逆变类型
}
impl<T: Copy> MyCell<T> {
    fn new(x: T) -> MyCell<T> {
        MyCell { value: x , mark: PhantomData}
    }
   fn get(&self) -> T {
       self.value
   }
   fn set(&self, value: T) {
       use std::ptr;
       unsafe {
           ptr::write(&self.value as *const _ as *mut _, value);
       }
   }
}
fn step1<'a>(r_c1: &MyCell<&'a i32>) {
    let val: i32 = 13;
    step2(&val, r_c1); // error[E0597]: `val` does not live long enough
    println!("step1 value: {}", r_c1.value);
} // step1调用完,栈帧将被清理,val将不复存在,&val将成为悬垂指针

fn step2<'b>(r_val: &'b i32, r_c2: &MyCell<&'b i32>) {
    r_c2.set(r_val);
}
static X: i32 = 10;
fn main() {
   let cell = MyCell::new(&X);
   step1(&cell);
   println!("  end value: {}", cell.value);
}Run

Basic usage: fn(T)逆变示意

Rust中仅存在函数指针fn(T)的逆变情况

请配合书本解释理解

trait A {
    fn foo(&self, s: &'static str);
}
struct B;
impl A for B {
    fn foo(&self, s: &str){
        println!("{:?}", s);
    }
}
impl B{
   fn foo2(&self, s: &'static str){
       println!("{:?}", s);
   }
}
fn main() {
   B.foo("hello");
   // let s = "hello".to_string();
  // B.foo2(&s)
}Run

Basic usage: 另一个fn(T)逆变示意

Rust中仅存在函数指针fn(T)的逆变情况

请配合书本解释理解

fn foo(input: &str)  {
    println!("{:?}", input);               
}
fn bar(f: fn(&'static str), v: &'static str) {
    (f)(v);
}
fn main(){
    let v : &'static str = "hello";
    bar(foo, v);
}Run