pub struct QuantityFunction<T>where
T: IsQuantity,{ /* private fields */ }Expand description
A thin wrapper around a Box<dyn IsQuantityFunction> trait object which provides
some type checks for usage in VarQuantity.
This struct wraps a Box<dyn IsQuantityFunction> so it can be used in the
VarQuantity::Function enum variant. As explained in the IsQuantityFunction
docstring, the unit of the DynQuantity returned by IsQuantityFunction::call
must always be the same. Even though this can unfortunately not be represented
by the type system for reasons outlined in the trait docstring, this wrapper
provides some checks to reduce the likelihood of wrong units:
- When constructing the wrapper via
QuantityFunction::new, it runsIsQuantityFunction::callonce with an empty slice and checks that the output unit matches that ofT::unit_from_type. If that is not the case, the construction fails and an error is returned. - When calling the underlying function via
QuantityFunction::call, it tries to convert theDynQuantity<f64>delivered fromIsQuantityFunction::callintoT. If that fails, the implementation ofIsQuantityFunctionviolates the requirement outlined in the trait documentation. This is a bug, hence the function panics.
This struct has the same memory representation as Box<dyn IsQuantityFunction>.
The underlying trait object can be retrieved directly via
QuantityFunction::into_inner or accessed via AsRef::as_ref
and Deref::deref.
§Features
This struct can be serialized / deserialized if the serde feature is enabled.
Since it is just a wrapper around a Box<dyn IsQuantityFunction> trait object,
it serializes directly to the representation of that object and deserializes
directly from it (it is“transparent”).
Implementations§
Source§impl<T> QuantityFunction<T>where
T: IsQuantity,
impl<T> QuantityFunction<T>where
T: IsQuantity,
Sourcepub fn new(
function: Box<dyn IsQuantityFunction>,
) -> Result<QuantityFunction<T>, UnitsNotEqual>
pub fn new( function: Box<dyn IsQuantityFunction>, ) -> Result<QuantityFunction<T>, UnitsNotEqual>
Creates a new instance of Self and performs a type safety check by running
the IsQuantityFunction::call of function with an empty slice as
conditions. The unit of the resulting DynQuantity is then
compared to that created by T::unit_from_type.
If they don’t match, an error is returned. See the docstring of
QuantityFunction for more.
§Examples
use var_quantity::{DynQuantity, PredefUnit, Unit};
use var_quantity::{IsQuantityFunction, QuantityFunction};
use var_quantity::uom::si::f64::{ElectricalResistance, ElectricCurrent};
// The serde annotations are just here because the doctests of this crate use
// the serde feature - they are not needed if the serde feature is disabled.
#[derive(Clone, serde::Deserialize, serde::Serialize, PartialEq)]
struct Resistance;
// Again, the macro annotation is just here because of the serde feature
#[typetag::serde]
impl IsQuantityFunction for Resistance {
fn call(&self, conditions: &[DynQuantity<f64>]) -> DynQuantity<f64> {
return DynQuantity::new(1.0, PredefUnit::ElectricResistance);
}
fn dyn_eq(&self, other: &dyn IsQuantityFunction) -> bool {
(other as &dyn std::any::Any).downcast_ref::<Self>() == Some(self)
}
}
let resistance = Resistance {};
// The Resistance struct always returns an electric resistance. Hence the
// type check fails for other types
assert!(QuantityFunction::<ElectricalResistance>::new(Box::new(resistance.clone())).is_ok());
assert!(QuantityFunction::<f64>::new(Box::new(resistance.clone())).is_err());
assert!(QuantityFunction::<ElectricCurrent>::new(Box::new(resistance.clone())).is_err());Sourcepub fn call(&self, conditions: &[DynQuantity<f64>]) -> T
pub fn call(&self, conditions: &[DynQuantity<f64>]) -> T
Forwards the input to the IsQuantityFunction::call method of the wrapped
trait object and asserts that the returned value can be converted to T.
If that is not the case, the constraint outlined in the docstring of
QuantityFunction is not fulfilled and the code is invalid, therefore
the function panics.
§Examples
This is a valid implementation of IsQuantity: Unit is always the
same regardless of input.
use var_quantity::{DynQuantity, PredefUnit, Unit, IsQuantityFunction, QuantityFunction};
use var_quantity::uom::si::electrical_resistance::ohm;
use var_quantity::uom::si::f64::{ElectricalResistance};
// The serde annotations are just here because the doctests of this crate use
// the serde feature - they are not needed if the serde feature is disabled.
#[derive(Clone, serde::Deserialize, serde::Serialize, PartialEq)]
struct Resistance;
// Again, the macro annotation is just here because of the serde feature
#[typetag::serde]
impl IsQuantityFunction for Resistance {
fn call(&self, conditions: &[DynQuantity<f64>]) -> DynQuantity<f64> {
return DynQuantity::new(1.0, PredefUnit::ElectricResistance);
}
fn dyn_eq(&self, other: &dyn IsQuantityFunction) -> bool {
(other as &dyn std::any::Any).downcast_ref::<Self>() == Some(self)
}
}
let wrapped_resistance = QuantityFunction::<ElectricalResistance>::new(Box::new(Resistance {})).expect("units match");
assert_eq!(ElectricalResistance::new::<ohm>(1.0), wrapped_resistance.call(&[1.0.into()]));This is an invalid (and nonsensical) implementation of IsQuantityFunction
where the output unit changes with the number of arguments:
use var_quantity::{DynQuantity, PredefUnit, Unit};
use var_quantity::{IsQuantityFunction, QuantityFunction};
use var_quantity::uom::si::f64::{ElectricalResistance};
// The serde annotations are just here because the doctests of this crate use
// the serde feature - they are not needed if the serde feature is disabled.
#[derive(Clone, serde::Deserialize, serde::Serialize, PartialEq)]
struct Resistance;
// Again, the macro annotation is just here because of the serde feature
#[typetag::serde]
impl IsQuantityFunction for Resistance {
fn call(&self, conditions: &[DynQuantity<f64>]) -> DynQuantity<f64> {
if conditions.len() == 0 {
return DynQuantity::new(1.0, PredefUnit::ElectricResistance);
} else {
return DynQuantity::new(1.0, PredefUnit::None);
}
}
fn dyn_eq(&self, other: &dyn IsQuantityFunction) -> bool {
(other as &dyn std::any::Any).downcast_ref::<Self>() == Some(self)
}
}
// Construction succeeds since the test call is done with an empty slice
let wrapped_resistance = QuantityFunction::<ElectricalResistance>::new(Box::new(Resistance {})).expect("units match");
// ... but calling with a quantity results in a panic
let _ = wrapped_resistance.call(&[DynQuantity::new(1.0, PredefUnit::None)]);Sourcepub fn into_inner(self) -> Box<dyn IsQuantityFunction>
pub fn into_inner(self) -> Box<dyn IsQuantityFunction>
Returns the underlying boxed IsQuantityFunction trait object.
Trait Implementations§
Source§impl<T> AsRef<dyn IsQuantityFunction> for QuantityFunction<T>where
T: IsQuantity,
impl<T> AsRef<dyn IsQuantityFunction> for QuantityFunction<T>where
T: IsQuantity,
Source§fn as_ref(&self) -> &(dyn IsQuantityFunction + 'static)
fn as_ref(&self) -> &(dyn IsQuantityFunction + 'static)
Source§impl<T> Clone for QuantityFunction<T>where
T: IsQuantity,
impl<T> Clone for QuantityFunction<T>where
T: IsQuantity,
Source§fn clone(&self) -> QuantityFunction<T>
fn clone(&self) -> QuantityFunction<T>
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more