特征能否保证某些类型属性(例如向量)为非空? [英] Can a trait guarantee certain type properties such as a vector is non-empty?

查看:14
本文介绍了特征能否保证某些类型属性(例如向量)为非空?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象一下我有这样的功能:

fn min_max_difference(row: &Vec) ->u32 {让 mut min_elem: u32 = row[0];让 mut max_elem: u32 = min_elem;对于 & 元素在 row.iter().skip(1) {如果元素<最小元素{min_elem = 元素;} else if 元素 >最大元素{max_elem = 元素;}}结果 = max_elem - min_elem;}fn execute_row_operation(row: &Vec, operation: Fn(&Vec) -> u32) ->选项<(u32, u32)>{让 mut 结果 = 无;如果 row.len() >0 {结果 = 操作(行);}结果}

注意execute_row_operation中的if块保证了Vec我传递给operation> 函数非空.一般来说,我希望操作"是接受非空行的函数.如果我能做这样的事情,我会喜欢它:

fn min_max_difference+ NonEmpty>(行:T)->u32 {//剪断}

这将允许编译器禁止将空向量的引用传递给像 min_max_difference 这样的函数.

但是我理解的traits指定了一个类型有什么方法,而不是一个类型有什么属性已.在我的脑海中,我正在想象 T 类型的特征,它由布尔谓词组成,类型为:Fn<T>->bool,如果所有这些谓词的计算结果都为真,那么这样的特征就是为一个类型实现"的.

这样的事情可以实现吗?

解决方案

一个 trait 能否保证某些类型的属性

是的,这就是他们的目的.在许多情况下,这些属性是存在一组函数(例如 PartialEq::eq) 并且存在一组行为(例如,PartialEq 要求的对称和传递相等).

Traits 也可以没有方法,比如 Eq.这些添加了一组行为(例如自反平等).这些类型的特征通常被称为标记特征.

<块引用>

比如一个向量是非空的?

然而,你并不是在要求你真正想要的东西.您实际上想要一种方法来为类型的某些 实现特征.这在 Rust 中是不可能的.

充其量,您可以引入一个新类型.这可能足以满足您的需求,但如果有用,您也可以为该新类型实现自己的标记特征:

struct NonEmptyVec(Vec);实施<T>NonEmptyVec T{fn new(v: Vec ) ->结果 <Self,Vec T >{如果 v.is_empty() {错误(五)} 别的 {好的(NonEmptyVec(v))}}}fn do_a_thing(项目:NonEmptyVec){}fn 主(){让 mut a = Vec::new();//do_a_thing(a);//预期结构体`NonEmptyVec`,找到结构体`std::vec::Vec`a.push(42);让 b = NonEmptyVec::new(a).expect("nope");do_a_thing(b);}

<块引用>

T: &Vec+ 非空

这是无效的,因为 Vec 是一种类型,而 NonEmpty 可能是一个特征——你不能使用类型作为特征边界.

<小时>

历史记录:

很久以前,据我所知,Rust 实际上确实支持您在 typestate 名称下想要的东西.请参阅什么是类型状态?Typestate 已死,Typestate 万岁!.

模拟它的一个例子:

struct MyVec在哪里S:VecState,{vec:Vec T ,状态,}特征 VecState {}结构空;结构体非空;impl VecState for Empty {}impl VecState for NonEmpty {}实施<T>MyVec{fn new() ->自己 {我的维克{vec: vec::new(),状态:空,}}fn push(mut self, value: T) ->MyVec{self.vec.push(value);我的维克{vec: self.vec,状态:非空,}}}fn do_a_thing(items: MyVec) {}fn 主(){让 a = MyVec::new();//do_a_thing(a);//预期结构体`NonEmpty`,找到结构体`Empty`让 b = a.push(42);do_a_thing(b);}

Imagine I have functions like this:

fn min_max_difference(row: &Vec<u32>) -> u32 {
    let mut min_elem: u32 = row[0];
    let mut max_elem: u32 = min_elem;

    for &element in row.iter().skip(1) {
        if element < min_elem {
            min_elem = element;
        } else if element > max_elem {
            max_elem = element;
        }
    }

    result = max_elem - min_elem;
}

fn execute_row_operation(row: &Vec<u32>, operation: Fn(&Vec<u32>) -> u32) -> Option<(u32, u32)> {
    let mut result = None;

    if row.len() > 0 {
        result = operation(row);
    }

    result
}

Note that the if block in execute_row_operation guarantees that the Vec<u32> I am passing to the operation function is non-empty. In general, I want "operations" to be functions which only accept non-empty rows. I would like it if I could do something like this:

fn min_max_difference<T: &Vec<u32> + NonEmpty>(row: T) -> u32 {
    //snip
}

This would allow the compiler to disallow passing references to empty vectors to a function like min_max_difference which expects this.

But traits as I understand them specify what methods a type has, rather than what properties a type has. In my head, I am imagining a trait for a type T that is composed of boolean predicates with type: Fn<T> -> bool, and such a trait is "implemented" for a type if it all those predicates evaluate to true.

Can something like this be achieved?

解决方案

Can a trait guarantee certain type properties

Yes, that is what they are for. In many cases, these properties are that a set of functions exist (e.g. PartialEq::eq) and that a set of behaviors are present (e.g. symmetric and transitive equality, required by PartialEq).

Traits can also have no methods, such as Eq. These only add a set of behaviors (e.g. reflexive equality). These types of traits are often referred to as marker traits.

such as a vector is non-empty?

However, you aren't asking for what you really want. You actually want a way to implement a trait for certain values of a type. This is not possible in Rust.

At best, you can introduce a newtype. This might be sufficient for your needs, but you could also implement your own marker traits for that newtype, if useful:

struct NonEmptyVec<T>(Vec<T>);

impl<T> NonEmptyVec<T> {
    fn new(v: Vec<T>) -> Result<Self, Vec<T>> {
        if v.is_empty() {
            Err(v)
        } else {
            Ok(NonEmptyVec(v))
        }
    }
}

fn do_a_thing<T>(items: NonEmptyVec<T>) {}

fn main() {
    let mut a = Vec::new();
    // do_a_thing(a); // expected struct `NonEmptyVec`, found struct `std::vec::Vec`
    a.push(42);
    let b = NonEmptyVec::new(a).expect("nope");
    do_a_thing(b);
}

T: &Vec<u32> + NonEmpty

This isn't valid because Vec is a type and NonEmpty would presumably be a trait — you can't use types as trait bounds.


Historical note:

Way back in the long ago, as I understand it, Rust actually did support what you wanted under the name typestate. See What is typestate? and Typestate Is Dead, Long Live Typestate!.

An example of emulating it:

struct MyVec<T, S>
where
    S: VecState,
{
    vec: Vec<T>,
    state: S,
}

trait VecState {}

struct Empty;
struct NonEmpty;

impl VecState for Empty {}
impl VecState for NonEmpty {}

impl<T> MyVec<T, Empty> {
    fn new() -> Self {
        MyVec {
            vec: Vec::new(),
            state: Empty,
        }
    }

    fn push(mut self, value: T) -> MyVec<T, NonEmpty> {
        self.vec.push(value);
        MyVec {
            vec: self.vec,
            state: NonEmpty,
        }
    }
}

fn do_a_thing<T>(items: MyVec<T, NonEmpty>) {}

fn main() {
    let a = MyVec::new();
    // do_a_thing(a); // expected struct `NonEmpty`, found struct `Empty`
    let b = a.push(42);
    do_a_thing(b);
}

这篇关于特征能否保证某些类型属性(例如向量)为非空?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆