pub struct EvolveBox<S: List<P>, P = ()> { /* private fields */ }Expand description
A pointer type which allows for safe transformations of its content without reallocation.
An EvolveBox has the same size as a Box and has the smallest size and alignment on the heap needed
to store its largest possible variant.
Therefore EvolveBox should be a zero cost abstraction,
meaning that there should be no runtime difference between a Box pointing at an untagged union and an EvolveBox,
while using an EvolveBox is also safe.
The size and alignment of the allocated memory is stored in the type S, which is a list of all used types,
as it is required for deallocation.
§Examples
Using EvolveBox inside of a function.
use evobox::{EvolveBox, L};
let s: EvolveBox<L<&str, L<String, L<u32>>>> = EvolveBox::new("7");
let owned = s.evolve(|v| v.to_string());
assert_eq!(owned.as_str(), "7");
let seven = owned.try_evolve(|s| s.parse()).expect("invalid integer");
assert_eq!(*seven, 7);Storing it in a generic struct.
use evobox::{EvolveBox, List, L};
enum Ty {
Integer,
String,
Boolean,
}
#[derive(Debug)]
struct TypeError;
struct Variable<'a, N, T>
where
L<&'a str, L<usize>>: List<N>,
L<&'a str, L<Ty>>: List<T>,
{
name: EvolveBox<L<&'a str, L<usize>>, N>,
ty: EvolveBox<L<&'a str, L<Ty>>, T>,
}
impl<'a> Variable<'a, (), ()> {
fn new(name: &'a str, ty: &'a str) -> Self {
Self {
name: EvolveBox::new(name),
ty: EvolveBox::new(ty),
}
}
}
impl<'a, T> Variable<'a, (), T>
where
L<&'a str, L<Ty>>: List<T>,
{
fn resolve_names(self, names: &mut Vec<&'a str>) -> Variable<'a, L<()>, T> {
let id = names.len();
let name = self.name.evolve(|name| {
names.push(name);
id
});
Variable { name, ty: self.ty }
}
}
impl<'a, N> Variable<'a, N, ()>
where
L<&'a str, L<usize>>: List<N>,
{
fn resolve_types(self) -> Result<Variable<'a, N, L<()>>, TypeError> {
let ty = self.ty.try_evolve(|ty|
match ty {
"int" => Ok(Ty::Integer),
"string" => Ok(Ty::String),
"bool" => Ok(Ty::Boolean),
_ => Err(TypeError)
}
)?;
Ok(Variable { name: self.name, ty })
}
}
let a = Variable::new("a", "int");
let b = Variable::new("b", "string");
let mut names = Vec::new();
let a = a.resolve_names(&mut names);
let _ = a.resolve_types().expect("unknown type");
let b = b.resolve_types().expect("unknown type");
let _ = b.resolve_names(&mut names);Implementations§
Source§impl<S: List<P>, P> EvolveBox<S, P>
impl<S: List<P>, P> EvolveBox<S, P>
Sourcepub fn into_inner(self) -> S::Value
pub fn into_inner(self) -> S::Value
Consumes this pointer, returning the current value.
§Examples
let evolve_box = EvolveBox::<L<_>>::new("hi");
assert_eq!("hi", evolve_box.into_inner());Source§impl<S: List<P>, P> EvolveBox<S, P>
impl<S: List<P>, P> EvolveBox<S, P>
Sourcepub fn evolve<F>(self, f: F) -> EvolveBox<S, L<P>>
pub fn evolve<F>(self, f: F) -> EvolveBox<S, L<P>>
Converts the current value to the next one without requiring a new allocation.
§Examples
let int_box: EvolveBox<L<u32, L<String>>> = EvolveBox::new(7);
let str_box = int_box.evolve(|i| format!("{}", i));
assert_eq!(str_box.as_str(), "7");Sourcepub fn try_evolve<F, E>(self, f: F) -> Result<EvolveBox<S, L<P>>, E>
pub fn try_evolve<F, E>(self, f: F) -> Result<EvolveBox<S, L<P>>, E>
Tries to convert the current value to the next one without requiring a new allocation. The error is propagated outwards in case the conversion fails.
§Examples
use std::convert::TryFrom;
let origin: EvolveBox<L<u32, L<u16, L<u8>>>> = EvolveBox::new(256);
let success = origin.try_evolve(u16::try_from).unwrap();
assert_eq!(*success, 256);
let error = success.try_evolve(u8::try_from);
assert!(error.is_err());