I meanwhile decided to do just that. I.e. the fallible conversion to &str
just just uses a trait method now and won't support TryFrom
/TryInto
at all. Note that all remaining bounds are on Self
(i.e. supertraits), which hopefully keeps most compiler bugs away! When converting owned values, I need to use .try_into()
instead of String::try_from(…)
) though.
This results in the following use in regard to fallible conversion from &Datum
to &str
:
use std::rc::Rc;
use std::cell::RefCell;
use my_sandboxing_system::prelude::*;
use my_sandboxing_system::{Callback, Compile, Machine};
fn collect_output<'a, 'b, M, C>(
machine: &'b M,
setup: C,
run: C,
) -> Result<String, MachineError>
where
M: Machine<'a> + Compile<'a, C> + Callback<'a>,
for<'c> <M as Machine<'a>>::Datum<'b, 'c>: MaybeString<'c>,
{
let output_cell = RefCell::new(String::new());
let output_rc = Rc::new(output_cell);
let output_weak = Rc::downgrade(&output_rc);
let my_print = machine
.callback_1arg(move |s| {
output_weak
.upgrade()
.ok_or("closure expired")?
.borrow_mut()
.push_str(s.try_as_str()?);
Ok([])
})?;
machine.compile(None, setup)?.call([my_print])?;
machine.compile(None, run)?.call([])?;
Ok(Rc::try_unwrap(output_rc).unwrap().into_inner())
}
By the way, the .call([…])
syntax with variable arguments has been realized through:
fn call<'c, A>(&self, args: A) -> Result<Vec<Self::Datum<'static>>, MachineError>
where
A: IntoIterator<Item = Self::Datum<'c>>,
<A as IntoIterator>::IntoIter: ExactSizeIterator;
This allows passing either a Vec<T>
or an [T; _]
, as I don't really depend on having a Vec
available (but only need .len()
and being able to iterate over the values). Not sure if that's idiomatic, but I like the shortened syntax when passing a variable number of arguments.