Rust 枚举/结构体:接受切片和向量

3

我有一些类似于这样的代码

enum Value<'a> {
    Int(i64),
    Flt(f64),
    Vec(&'a [Value<'a>]),
}

这使我可以重复使用一些数据。但是,有时我想接受堆分配的数据,所以我需要类似于这样的东西。

enum Value {
   Int(i64),
   Flt(f64),
   Vec(Box<Vec<Value>>),
}

但现在我无法接受片段!我知道我可以把它们都放在同一个枚举中,像这样

enum Value<'a> {
   Int(i64),
   Flt(f64),
   VecSlice(&'a [Value<'a>]),
   VecBox(Box<Vec<Value<'a>>>),
}

但这非常丑陋。

有没有一种方法可以在同一个成员/变体中接受切片和向量?

我知道对于接受 &str 和 String 的函数,我们可以将参数设置为类似于 T: Into<String> 的东西,但我还没有想出如何在数据类型中处理向量的方法。


2
Box<Vec<Value>> 这个语句没有意义,它是将一个向量装箱。 - Stargateur
@Stargateur 如果枚举内部的向量未装箱,则枚举大小将为32字节,而不是装箱时的16字节。在某些情况下,这可能非常重要。 - Joan Vene
@Stargateur 你说得对!我刚刚尝试从函数返回一个未装箱的向量,一切都很顺利。问题在于我没有为向量指定生命周期,因此使用了 Box。谢谢 :) - Joan Vene
@Stargateur 但是有没有办法将大小减小到16?现在是24。这不是什么大问题,但我想问一下。 - Joan Vene
1
我认为切片不需要胖指针,枚举也需要一些空间,但请注意不要过于担心这个问题,在编写代码后再观察是否存在问题。 - Stargateur
2个回答

2
你想要的是Cow
enum Value<'a> {
    Int (i64),
    Flt (f64),
    Vec (Cow<'a, [Value<'a>]>),
}

很遗憾,这个不能工作,因为#38962。在解决了这个问题之前,您可能需要重新实现一个专门的 Cow 版本来处理 Value
enum MyCow<'a> {
    Borrowed (&'a[Value<'a>]),
    Owned (Vec<Value<'a>>)
}

impl<'a> Deref for MyCow<'a> {
    type Target = [Value<'a>];
    fn deref (&self) -> &[Value<'a>] {
        use crate::MyCow::{ Borrowed, Owned };
        match *self {
            Borrowed (borrowed) => borrowed,
            Owned (ref owned) => &owned,
        }
    }
}

游乐场


在使用 Cow 方面似乎有一个 变通方法 - 我还没有尝试将其应用到这种情况。 - user4815162342

1

我认为最接近你想要的东西是AsRef trait。值得注意的是,Vec<T>[T][T;n](其中n <= 32)实现了AsRef<[T]>,还有一些其他的东西(比如一个切片上的迭代器)。此外,Box<T>实现了AsRef<T>,但是你的Box<Vec<T>>场景在这里不太适用。不过,当涉及到枚举类型时会变得有点棘手。类型描述并不完全适用于此:

enum Value<S>
    where S: AsRef<[Value<S>]>
{
    Int(i64),
    Flt(f64),
    Slice(S),
}

因为你致力于一次只实例化一个 S,而要修复这个问题需要使用一个 Box<dyn S> 使其多样化,这会变得非常混乱。
如果你可以重构代码以在函数级别上工作,或者创建一个高于 Value 的更高级类型,那么你就可以有像这样的函数。
fn foo<S>(slice: S) where S: AsRef<[Value]> { }

使用这种结构相当容易。在这种情况下,如果您有一个Box<Vec<Value>>,那么调用foo(my_vec)不会完全起作用,但可以通过解引用轻松修复,因为Box<[T]>实现了From<Vec<T>>

use std::convert::AsRef;

enum Value
{
    Int(i64),
    Flt(f64),
}

fn main() {
    use Value::*;

    let x = Box::new(vec![Int(5),Flt(22.5),Int(22)]);
    foo(*x)
}

fn foo<S>(slice: S) where S: AsRef<[Value]> {

}

游乐场


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接