特征能否保证某些类型属性(例如向量非空)?
Can a trait guarantee certain type properties such as a vector is non-empty?
假设我有这样的函数:
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
}
请注意,execute_row_operation
中的 if
块保证我传递给 operation
函数的 Vec<u32>
是非空的。通常,我希望 "operations" 成为 仅 接受非空行的函数。如果我能做这样的事情,我会很高兴:
fn min_max_difference<T: &Vec<u32> + NonEmpty>(row: T) -> u32 {
//snip
}
这将允许编译器禁止将对空向量的引用传递给像 min_max_difference
这样期望这样的函数。
但是 traits as I understand them 指定类型具有哪些方法,而不是类型具有哪些属性。在我的脑海中,我正在想象一个类型 T
的特征,它由类型为 Fn<T> -> bool
的布尔谓词组成,如果所有这些谓词都包含,那么这个特征就是一个类型的 "implemented"评估为真。
这样的东西能实现吗?
Can a trait guarantee certain type properties
是的,这就是他们的目的。在许多情况下,这些属性是存在一组函数(例如 PartialEq::eq
)并且存在一组行为(例如 PartialEq
要求的对称和传递相等性)。
Traits也可以没有方法,比如Eq
。这些 only 添加了一组行为(例如反身平等)。这些类型的特征通常被称为 标记特征 .
such as a vector is non-empty?
但是,您并不是在要求您真正想要的东西。您实际上想要一种方法来实现某种类型的某些 值 的特征。这在 Rust 中是不可能的。
充其量,你可以引入一个新类型。这可能足以满足您的需求,但如果有用的话,您也可以为该新类型实现自己的标记特征:
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
这是无效的,因为 Vec
是一种类型,而 NonEmpty
可能是一种特征 — 您不能将类型用作特征边界。
历史记录:
很久以前,据我了解,Rust 实际上 确实 支持您在名称 typestate 下想要的东西。参见 What is typestate? and Typestate Is Dead, Long Live Typestate!。
一个模拟它的例子:
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);
}
假设我有这样的函数:
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
}
请注意,execute_row_operation
中的 if
块保证我传递给 operation
函数的 Vec<u32>
是非空的。通常,我希望 "operations" 成为 仅 接受非空行的函数。如果我能做这样的事情,我会很高兴:
fn min_max_difference<T: &Vec<u32> + NonEmpty>(row: T) -> u32 {
//snip
}
这将允许编译器禁止将对空向量的引用传递给像 min_max_difference
这样期望这样的函数。
但是 traits as I understand them 指定类型具有哪些方法,而不是类型具有哪些属性。在我的脑海中,我正在想象一个类型 T
的特征,它由类型为 Fn<T> -> bool
的布尔谓词组成,如果所有这些谓词都包含,那么这个特征就是一个类型的 "implemented"评估为真。
这样的东西能实现吗?
Can a trait guarantee certain type properties
是的,这就是他们的目的。在许多情况下,这些属性是存在一组函数(例如 PartialEq::eq
)并且存在一组行为(例如 PartialEq
要求的对称和传递相等性)。
Traits也可以没有方法,比如Eq
。这些 only 添加了一组行为(例如反身平等)。这些类型的特征通常被称为 标记特征 .
such as a vector is non-empty?
但是,您并不是在要求您真正想要的东西。您实际上想要一种方法来实现某种类型的某些 值 的特征。这在 Rust 中是不可能的。
充其量,你可以引入一个新类型。这可能足以满足您的需求,但如果有用的话,您也可以为该新类型实现自己的标记特征:
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
这是无效的,因为 Vec
是一种类型,而 NonEmpty
可能是一种特征 — 您不能将类型用作特征边界。
历史记录:
很久以前,据我了解,Rust 实际上 确实 支持您在名称 typestate 下想要的东西。参见 What is typestate? and Typestate Is Dead, Long Live Typestate!。
一个模拟它的例子:
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);
}