How can I lazy initialize/fill an `Option` with a fallible initializer? - rust

Suppose I have a struct containing an Option<Resource>, where Resource is some type I need to work with that needs to allocate external resources (in the actual case, GPU memory) and so might fail. From within a method, I want to attempt the allocation if it hasn't been done already, but propagate or handle the error if it does fail.
If it weren't for the failure case, Option::get_or_insert_with would be perfect. As it is, the tidiest solution I have thought of involves an unwrap(), which is inelegant since it looks like a potential panic:
struct Container {
resource: Option<Resource>,
...
}
impl Container {
...
fn activate(&mut self) -> Result<(), Error> {
if self.resource.is_none() {
self.resource = Some(Resource::new()?);
}
let resource: &mut Resource = self.resource.as_mut().unwrap();
// ... now do things with `resource` ...
Ok(())
}
...
}
Is there a way to initialize the Option with less fuss than this? To be clear, I'm not solely looking for avoiding unwrap(), but also overall readability. If an alternative is much more complex and indirect, I'd rather stick with this.
Complete example code (on Rust Playground):
#[derive(Debug)]
struct Resource {}
#[derive(Debug)]
struct Error;
impl Resource {
fn new() -> Result<Self, Error> {
Ok(Resource {})
}
fn write(&mut self) {}
}
#[derive(Debug)]
struct Container {
resource: Option<Resource>,
}
impl Container {
fn new() -> Self {
Self { resource: None }
}
fn activate(&mut self) -> Result<(), Error> {
if self.resource.is_none() {
self.resource = Some(Resource::new()?);
}
self.resource.as_mut().unwrap().write();
Ok(())
}
}
fn main() {
Container::new().activate();
}

Indeed, get_or_insert_with() but returning Result<&mut T, E> is what you could use to simplify your code. However, as you already discovered Option doesn't have a e.g. try_get_or_insert_with() method.
Other workarounds would be similarly verbose. However, you could use a match and get_or_insert(), to avoid the unwrap() like this:
fn activate(&mut self) -> Result<(), Error> {
let res = match &mut self.resource {
Some(res) => res,
None => self.resource.get_or_insert(Resource::new()?),
};
res.write();
Ok(())
}
If this is used frequently, then you could also define your own try_get_or_insert_with() trait method, and implement it for Option<T>.
trait TryGetOrInsert<T> {
fn try_get_or_insert_with<E, F>(&mut self, f: F) -> Result<&mut T, E>
where
F: FnOnce() -> Result<T, E>;
}
impl<T> TryGetOrInsert<T> for Option<T> {
fn try_get_or_insert_with<E, F>(&mut self, f: F) -> Result<&mut T, E>
where
F: FnOnce() -> Result<T, E>,
{
match self {
Some(value) => Ok(value),
None => Ok(self.get_or_insert(f()?)),
}
}
}
Then now you can simplify your activate() method, to the following:
fn activate(&mut self) -> Result<(), Error> {
let res = self.resource.try_get_or_insert_with(|| Resource::new())?;
res.write();
Ok(())
}

Related

Automatically implement a trait for a type that already satisfies it

TL;DR. Is it possible to automatically implement a trait for an existing type that already has all methods required by the trait?
Long version. Suppose I want to have a generic function that does stack operations on any stack-like type. So I have a trait
pub trait StackLike<T> {
fn is_empty(&self) -> bool;
fn pop(&mut self) -> Option<T>;
fn push(&mut self, value: T);
}
which I can now use like this: pub fn foo(s: &mut dyn StackLike<i32>) { s.push(42); }. So far so good.
There are several existing types that already satisfy my trait, so implementing it is trivial, e.g.
impl<T> StackLike<T> for Vec<T> {
fn is_empty(&self) -> bool { self.is_empty() }
fn pop(&mut self) -> Option<T> { self.pop() }
fn push(&mut self, value: T) { self.push(value) }
}
impl<T, const N: usize> StackLike<T> for SmallVec<[T; N]> {
fn is_empty(&self) -> bool { self.is_empty() }
fn pop(&mut self) -> Option<T> { self.pop() }
fn push(&mut self, value: T) { self.push(value) }
}
This works, but it's a lot of boilerplate. Absolutely nothing interesting happens here: method names, argument types and return type — everything is same.
Question. Is it possible to avoid spelling out all these tautological statements, i.e. do something like the snippet below?
#[implement-trait-automagically]
impl<T> StackLike<T> for Vec<T>;
#[implement-trait-automagically]
impl<T, const N: usize> StackLike<T> for SmallVec<[T; N]>;
Or may be I can mark my trait somehow so that all types that can satisfy it automatically satisfy it (similar to how concepts work in C++)?
Rust doesn't have this kind of "duck typing" but you can put the implementation behind a macro to achieve your stated goals of avoiding all of the boilerplate code while keeping the implementations identical and error-free:
pub trait StackLike<T> {
fn is_empty(&self) -> bool;
fn pop(&mut self) -> Option<T>;
fn push(&mut self, value: T);
}
macro_rules! stacklike_impl {
() => {
fn is_empty(&self) -> bool { self.is_empty() }
fn pop(&mut self) -> Option<T> { self.pop() }
fn push(&mut self, value: T) { self.push(value) }
}
}
impl<T> StackLike<T> for Vec<T> { stacklike_impl!(); }
impl<T, const N: usize> StackLike<T> for SmallVec<[T; N]> { stacklike_impl!(); }

Rust trait issues trait cannot be made into an object

I'm trying to write some code that will generate a random struct with a random value. I have the following trait and helper macros for the structs:
use rand::{thread_rng, Rng};
use std::fmt;
pub trait DataType {
/// generate a new instance of the type with a random value
fn random() -> Box<Self>;
/// generate a new instance of the same type with a random value
fn gen_another(&self) -> Box<Self>;
}
macro_rules! impl_data_type_for_num {
($x:ident) => {
impl DataType for $x {
fn random() -> Box<Self> {
Box::new(Self {
value: thread_rng().gen()
})
}
fn gen_another(&self) -> Box<Self> {
Self::random()
}
}
};
}
macro_rules! impl_formatting {
($x:ident, $s:expr) => {
impl fmt::Debug for $x {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, $s)
}
}
impl fmt::Display for $x {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
};
}
Then I use the macros to implement the needed traits on a bunch of structs (heres a few for example):
pub struct VirtBool {
value: bool
}
impl_data_type_for_num!(VirtBool);
impl_formatting!(VirtBool, "bool");
pub struct VirtU8 {
value: u8
}
impl_data_type_for_num!(VirtU8);
impl_formatting!(VirtU8, "u8");
pub struct VirtU16 {
value: u16
}
impl_data_type_for_num!(VirtU16);
impl_formatting!(VirtU16, "u16");
So far it all works fine, but then an issue arises when I try to implement the same traits on a struct with unsized fields:
pub struct VirtArray {
_type: Box<dyn DataType>,
value: Vec<Box<dyn DataType>>
}
impl DataType for VirtArray {
fn random() -> Box<Self> {
let t = random_var();
let s = thread_rng().gen_range(0, 10);
Box::new(Self {
_type: *t,
value: (0..s).map(|_| t.gen_another()).collect()
})
}
fn gen_another(&self) -> Box<Self> {
Box::new(Self {
_type: self._type,
value: self.value.iter().map(|t| t.gen_another()).collect::<Vec<Box<dyn DataType>>>()
})
}
}
impl fmt::Debug for VirtArray {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{:?}; {}]", self._type, self.value.len())
}
}
impl fmt::Display for VirtArray {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = self.value.iter().map(|v| format!("{}, ",v)).collect::<String>();
s.truncate(s.len().checked_sub(2).unwrap_or(s.len()));
write!(f, "[{}]", s)
}
}
This throws the error the trait 'DataType' cannot be made into an object on several occasions. I then have a function to create a random struct but that also throws the same error:
/// generate a random type with random value
fn random_var() -> Box<dyn DataType> {
match thread_rng().gen_range(0,4) {
0 => Box::new(VirtBool::random()),
1 => Box::new(VirtU8::random()),
2 => Box::new(VirtU16::random()),
3 => Box::new(VirtArray::random()),
_ => panic!("invalid")
}
}
I was originally using enums to do all of this but I'm trying to switch it over to structs and traits to help with scaling/use-ability. Does anyone have any idea how to fix the code above? I've been at a roadblock here for quite a while now.
Also, I'm aware I could use type_name_of_val to print the types, but I'm trying to keep from using unstable/nightly features.
DataType cannot be made into a trait object because it uses Self and because it has a static method.
I realize it might seem like returning Box<Self> may be reasonable to call on a dyn DataType, since if you call it on dyn DataType you want a Box<dyn DataType>, but Rust doesn't try to modify methods for you to turn methods that return e.g. Box<VirtArray> into ones that return Box<dyn DataType> if they are called on a dyn DataType value. You can work around this by having the methods return Box<dyn DataType> instead.
Static methods are not allowed for trait objects because there is no implementation for the trait object type. Remember, dyn Foo implements Foo. What would (dyn DataType)::random() be? (You can use a where clause, as in the example above, to make sure dyn DataType isn't expected to have this method in a way that can be detected ahead of time; this means you can't use it on your dyn DataType objects, but it sounds like you might not want to.)

Can a macro simplify trait impl?

I want to simplify files and indention levels for implementing several Traits for a given struct. Is there a way I can do something like this?
struct Foo { /* ommitted */ }
impl(Default(Foo)) fn default() -> Self {
/* do something */
}
Obviously, it is possible to write
impl Default for Foo {
fn default() -> Self {
/* do something */
}
}
but the enclosing text provides no more value than the macro I hope exists. Although I am new to Rust, I am well aware of (and frequently use)
#[derive(Default)]
but I am looking for a way to simplify actually implementing many traits. I would consider the following clear as well, but AFAIK it is not legal Rust. Note that the following is similar to rust-lang #1250:
impl Foo {
type Index::Output = Bar;
trait Default fn default() -> Self {
// ...
}
trait Debug fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// ...
}
trait Display fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// ...
}
trait Index<usize> fn index(&self, (r, c): (usize, usize)) -> &Self::Index::Output {
// ...
}
fn my_custom_thing() -> () {
// ...
}
}
Whenever I tried something like this, I found that it was easier to just write out the impls.
Thus, I would advise against it. But if you insist, you could start out with the following:
macro_rules! quick_impl {
($structname: ident, Default, $($t:tt)*) => {
impl Default for $structname {
fn default() -> Self {
Self {$($t)*}
}
}
};
($structname: ident, Debug, $($field:ident),*) => {
impl std::fmt::Debug for $structname {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
f.debug_struct(stringify!($tyname))
$(
.field(stringify!($field), &self.$field)
)*
.finish()
}
}
};
// here be more implementations
}
Usable like this:
struct S {
a: usize,
b: bool,
c: char,
}
quick_impl!(S, Default, a:1, b:true, c:'c');
quick_impl!(S, Debug, a, b, c);
This macro goes a step further in that you do not have to say trait or fn in the arguments (as this is clear anyway).

How to implement Display on a trait object where the types already implement Display

I have some code which returns a trait object of type MyTrait so that it can return one of several different structs. I would like to implement the Display trait for the trait object so that I can print the object, with the details delegated to the various structs as they each need their own custom formatters.
I can achieve this by including a formatting method as part of the MyTrait definition, and then implementing Display for MyTrait and delegating - like this:
trait MyTrait {
fn is_even(&self) -> bool;
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
impl fmt::Display for MyTrait {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.my_fmt(f)
}
}
However, I already have the Display trait implemented for each of the structs which implement MyTrait. This means I end up with two methods for each struct which do the same thing - the fmt() method to satisfy the Display trait directly on the struct, and the my_fmt() method which is called by the code above. This seems clumsy and repetitive. Is there a simpler way to do it?
Here's a complete example program which illustrates the point. It's a little longer than I would have liked (it's based on the answer to my previous question Calling functions which return different types with shared trait and pass to other functions), but I couldn't think of a simpler way to illustrate the point. Of course, in this toy example the structs and the fmt functions are very simple; in my real application they are more complex.
use std::fmt;
trait MyTrait {
fn is_even(&self) -> bool;
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
struct First {
v: u8,
}
struct Second {
v: Vec<u8>,
}
impl MyTrait for First {
fn is_even(&self) -> bool {
self.v % 2 == 0
}
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v)
}
}
impl MyTrait for Second {
fn is_even(&self) -> bool {
self.v[0] % 2 == 0
}
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v[0])
}
}
fn make1() -> First {
First { v: 5 }
}
fn make2() -> Second {
Second { v: vec![2, 3, 5] }
}
// Implement Display for the structs and for MyTrait
impl fmt::Display for First {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v)
}
}
impl fmt::Display for Second {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v[0])
}
}
impl fmt::Display for MyTrait {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.my_fmt(f)
}
}
fn requires_mytrait<T: MyTrait + ?Sized>(v: &&T) {
println!("{:?}", v.is_even());
}
fn main() {
for i in 0..2 {
let v1;
let v2;
let v = match i {
0 => {
v1 = make1();
println!("> {}", v1); // Demonstrate that Display
// is implemented directly
// on the type.
&v1 as &MyTrait
}
_ => {
v2 = make2();
println!("> {}", v2); // Demonstrate that Display
// is implemented directly
// on the type.
&v2 as &MyTrait
}
};
requires_mytrait(&v);
println!("{}", v); // Here I print the trait object
}
}
Can anyone suggest a simpler, cleaner way to do this?
You can make Display a supertrait of MyTrait.
trait MyTrait: fmt::Display {
fn is_even(&self) -> bool;
}
This will make trait objects of MyTrait be Display. This only works if you expect all implementors of MyTrait to implement Display, but that was also the case in your previous solution.

Using and_then with different Result error types without map_err

I have some functions that will return a different error type when failing.
First I have a builder, which contains this method:
#[derive(Debug)]
pub enum BuilderError {
ElementMissing(&'static str),
}
pub fn spawn(self) -> Result<ServiceStatus, BuilderError>
So it will return a BuildError on failure.
Now, I have another function that will return another error:
#[derive(Debug)]
pub enum XmlError {
XmlCreationFailed(writer::Error),
ConversionToUtf8(FromUtf8Error),
}
pub fn create_xml(service_status: super::ServiceStatus) -> Result<String, XmlError>
The idea is that I use the builder to create a ServiceStatus object and use it to create a XML string with create_xml function.
To do that, I have this code:
#[derive(Debug)]
pub enum WebserviceError {
XmlError(XmlError),
BuilderError(BuilderError),
}
impl std::error::Error for WebserviceError {
...
}
impl From<XmlError> for WebserviceError {
fn from(error: XmlError) -> WebserviceError {
WebserviceError::XmlError(error)
}
}
impl From<BuilderError> for WebserviceError {
fn from(error: BuilderError) -> WebserviceError {
WebserviceError::BuilderError(error)
}
}
fn test() -> Result<String, status::WebserviceError> {
...
let service_status = builder.spawn()?;
let xml = status::create_xml(service_status)?;
Ok(xml)
}
Now, I think I can do better using and_then instead of using ? operator:
fn test() -> Result<String, status::WebserviceError> {
...
builder
.spawn()
.map_err(status::WebserviceError::BuilderError)
.and_then(|hue| status::create_xml(hue).map_err(status::WebserviceError::XmlError))
}
This solution works too, but now I need to explicitly call map_err to convert from a BuilderError or XmlError to a WebserviceError...
So, my question is, can I do better? I think a solution like this would be ideal:
fn test() -> Result<String, status::WebserviceError> {
...
builder
.spawn()
.and_then(status::create_xml)
}
After some trial, here is the solution:
trait CustomAndThen<T, E> {
fn and_then2<U, E2, F: FnOnce(T) -> Result<U, E2>>(self, op: F) -> Result<U, E>
where E: std::convert::From<E2>;
}
impl<T, E> CustomAndThen<T, E> for Result<T, E> {
fn and_then2<U, E2, F: FnOnce(T) -> Result<U, E2>>(self, op: F) -> Result<U, E>
where E: std::convert::From<E2>
{
match self {
Ok(t) => op(t).map_err(From::from),
Err(e) => Err(e),
}
}
}
...
Ok(builder)
.and_then2(status::ServiceStatusBuilder::spawn)
.and_then2(status::create_xml)
This will create a custom and_then function for Result type that will make the conversion inside it, clearing the code
If you're not really interested in exact error, but raise some final error in the end you can use something like:
builder.spawn().ok()
.and_then(|v| status.create_xml(v).ok())
.ok_or_else(|| SomeError('failed to create xml'))

Resources