我需要任何* variantType标记吗?

时间:2014-08-18 14:10:37

标签: rust ffi type-safety

我试图以类型安全的方式与Rust交互一个类型不安全的C库。 C接口处理一些void指针。具体来说,有一个write和一个read函数,它将void指针作为参数并执行明显的操作(将给定数据写入其他位置或从该位置读取到给定的缓冲区)。这是什么类型的数据,早先同意将某个整数值加到open函数

1 = float
2 = int32_t
4 = int16_t
etc...

我用一个参数化的Rust结构表示一个打开的输出流(用户可以调用write),如下所示:

pub struct OutputStream<T> {
    c_stream: *mut ()
}

impl<T: ValueType> for OutputStream<T> {
    pub fn write(&mut self, data: &[T]) -> Result<(),SomeErrorType> {
        unsafe { ... }
    }
}

其中ValueType是我为所有受支持的值类型编写的特征,其他任何人都无法实现它(私有基本特征技巧)。所以,显然在结构中的任何地方都没有提到T.这似乎是ContravariantType标记的一种情况。但我不太了解这些类型的标记。访问维基百科页面的差异没有帮助。我只是没有看到方差之间的联系以及它如何适用于Rust的类型参数。而且我真的不知道这种标记会实际阻止什么。

所以我的问题是:我的OutputStream是否需要其中一种标记?如果是这样,哪一个和为什么?使用它我究竟会阻止什么? InputStream提供read方法而不是write方法也是如此。

1 个答案:

答案 0 :(得分:1)

通常,方差确定参数化类型之间关于其参数的子类型关系:

Covariance:      T <: U    =>    F[T] <: F[U]
Contravariance:  T <: U    =>    F[U] <: F[T]
Invariance:      T <: U    =>    neither of the above
Bivariance:      T <: U    =>    both of the above

您的类型 是自然逆变的:它的方法只消耗类型为T的值,但不生成它们;这就是所谓的消费者类型。然而,Rust中的子类型非常有限;据我所知,允许任何类型的子类型关系的唯一类型是引用(例如,您可以将&'static str传递给&'a str变量,因为'static生命周期大于或等于任何其他生命周期,因此&'static str是任何&'a str的{​​{1}}子类型。

所以,如果我理解正确,你需要方差注释。如果您的'a参数可以作为参考,请使用T,这样您就可以执行此操作:

ContravariantType

但不能这样做:

fn push_something_to(os: OutputStream<&'static str>) { ... }

let s: OutputStream<&'a str> = ...;  // and 'a is less than 'static
push_something_to(s);   // this is safe to do because &'static str is valid &'a str

let s: OutputStream<int> = ...; push_something_to(s); // oops, push_something_to expects stream of &'static str 两者都被禁止。


然而,在我这边似乎存在一些深刻的误解,因为关于方差标记的文档中的代码,以及我自己的代码在我当前的Rust中都不起作用:

InvariantType

根据文档,这应该编译,因为参数化类型默认是双变量,但它不会:它就像参数是不变的一样。

这是我自己的例子:

use std::ptr;
use std::mem;

struct S<T> { x: *const () }

fn get<T>(s: &S<T>, v: T) {
   unsafe {
       let x: fn(T) = mem::transmute(s.x);
       x(v)
   }
}

fn main() {
    let s: S<int> = S { x: ptr::null() };
    get::<Box<int>>(&s, box 1);
}

据我所知,这个程序应该编译,但它不会:

#![allow(dead_code)]

type F<T> = fn(T);

fn test_1<'a>(f: F<&'a str>) -> F<&'static str> {
    f
}

struct S<T> {
    _m: std::kinds::marker::ContravariantType<T>
}

fn test_2<'a>(s: S<&'a str>) -> S<&'static str> {
    s
}

fn main() {}

如果我删除<anon>:9:5: 9:6 error: mismatched types: expected `S<&'static str>` but found `S<&'a str>` (lifetime mismatch) <anon>:9 s ^ <anon>:8:47: 10:2 note: the lifetime 'a as defined on the block at 8:46... <anon>:8 fn test<'a>(s: S<&'a str>) -> S<&'static str> { <anon>:9 s <anon>:10 } note: ...does not necessarily outlive the static lifetime S,它编译得很好,证明函数类型在参数类型方面确实是逆变的。

我不知道发生了什么,看起来它值得另一个问题。