Reputation: 99
I have some code similar to this
enum Value<'a> {
Int(i64),
Flt(f64),
Vec(&'a [Value<'a>]),
}
and this lets me reuse some data; however, some times I want to accept heap-allocated data, so I would need something like this
enum Value {
Int(i64),
Flt(f64),
Vec(Box<Vec<Value>>),
}
but now I cannot accept slices! I know I could always have both of them in the same enum, like this
enum Value<'a> {
Int(i64),
Flt(f64),
VecSlice(&'a [Value<'a>]),
VecBox(Box<Vec<Value<'a>>>),
}
but this is very ugly.
Is there a way to have a struct or enum that accepts both slices and vectors in the same member/variant?
I know that for functions accepting &str and String we can just set the parameters to something like T: Into<String>
but I have not figured out how to do something like this for vectors inside data types.
Upvotes: 3
Views: 2405
Reputation: 23414
What you want is Cow
:
enum Value<'a> {
Int (i64),
Flt (f64),
Vec (Cow<'a, [Value<'a>]>),
}
Unfortunately this doesn't work because of #38962. Until that issue is fixed, you may need to re-implement a specialized version of Cow
for 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,
}
}
}
Upvotes: 2
Reputation: 22216
I think the closest thing to what you want is the AsRef
trait. Notably, Vec<T>
, [T]
, and [T;n]
for n <= 32
implement AsRef<[T]>
, as do a few other things (like an iterator over a slice). Additionally, Box<T>
implements AsRef<T>
, but your scenario of Box<Vec<T>>
won't quite work here. The gets a little hairy with an enum, though. The type description doesn't quite work as:
enum Value<S>
where S: AsRef<[Value<S>]>
{
Int(i64),
Flt(f64),
Slice(S),
}
Because you're committed to instantiating exactly one S
at a time, and fixing that requires using a Box<dyn S>
to make it heterogeneous which gets really messy.
If you can refactor to make this work at the function level or create a higher level type above Value
, you can have functions like
fn foo<S>(slice: S) where S: AsRef<[Value]> { }
Fairly easily with this construction, however. In this case if you have a Box<Vec<Value>>
the invocation foo(my_vec)
won't quite work, but can trivially be fixed with dereferencing since Box<[T]>
implements 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]> {
}
Upvotes: 1