Open wellcaffeinated opened 5 years ago
The easiest way to do this is to accept something that can convert into Kelvin.
You will also need to call value()
(part of trait Dimensionless
) to get the underlying f64
value once you have a unitless quantity:
Example:
use dimensioned::{si, Dimensionless};
impl TemperatureDependence for MyStruct {
fn apply<T>(&self, n: Vector3<f64>, temperature: T) -> Vector<f64>
where T: Into<si::Kelvin<f64>> {
// --snip--
let f: f64 = *((temperature.into() - 20.0 * si::K) / si::K).value();
// --snip--
// returns a vector based on this
}
}
If you really want to accept anything that is a Temperature
without conversion, to which you'll add a constant, you'll probably end up having to do some hacks with the One
trait in num or something like it, and it is likely to be a lot more work than it's worth.
That's fantastic! Thanks. Everything makes sense to me except for dereferencing that computation.
Presumably those brackets return a reference... but i'm not sure why. And doesn't calling a method on a reference automatically dereference it?
value()
returns a reference. It should probably return a value, but it's too late to change without breaking changes.
Ohhh I see. I got the order of operations mixed up.
But now that I think about it, if I implement the function with this trait requirement and try to write another function that uses this one, won't that requirement ascend up the chain? Do I have to constantly worry about T: Into<si::Kelvin<f64>>
?
eg:
fn otherFn<Q, T>( &tempDep: Q, n: Vector3<f64>, temp: T) -> Vector3<f64>
where
Q: TemperatureDependence,
T: Into<si::Kelvin<f64>> // do I need to worry about this every time I use a temperature now?
{
tempPep.apply(n, temp)
}
value()
returns a reference. It should probably return a value, but it's too late to change without breaking changes.
For what it's worth, I would be happy to see see this change, and update my own code to deal with it. Overall, it would simplify things.
But now that I think about it, if I implement the function with this trait requirement and try to write another function that uses this one, won't that requirement ascend up the chain? Do I have to constantly worry about
T: Into<si::Kelvin<f64>>
?
Yeah, that's how generics in Rust work. Alternatively, those functions "up the chain" could accept any concrete type that implements that trait, such as taking si::Kelvin<f64>
directly.
For what it's worth, I would be happy to see see this change, and update my own code to deal with it. Overall, it would simplify things.
I have a few breaking changes in mind that I'd like to save until I can replace typenum with const generics. This is one of them.
Oh that's too bad. I was hoping this library would implement something more along the lines of this: https://ferrisellis.com/content/rust-implementing-units-for-types/
This strategy allows for accepting a Length in any units without declaring the input units.
fn circumference<T>(r: Length<T>) -> Length<T> where T: LengthUnit {
2 * r * std::f64::consts::PI
}
fn main() {
let l1 = millimeters!(10);
let l2 = meters!(5);
let l3 = (5 * l1) + l2;
let l3_meters = f64::from(meters!(l3));
let c1 = circumference(l1);
println!("l1 = {}", l1);
println!("l2 = {}", l2);
println!("l3 = (5 * l1) + l2 = {}", l3);
println!("l3_meters = {}", l3_meters);
println!("circumference(radius = {}) = {}", l1, c1);
println!("l3 > l2 : {}", l3 > l2);
println!("l3 / l2 = {}", l3 / l2);
}
This strategy allows for accepting a Length in any units without declaring the input units.
It allows the appearance of accepting a Length in any units. In reality, lengths are only represented as nanometers. If you need to operate with greater than nanometer precision, or at astronomical distances, that type is useless.
Dimenionsed takes an alternate approach, letting you represent values in any units you wish, with the precision you wish, with the trade-off of additional boilerplate for generic functions.
You could achieve something similar in Dimensioned by, for example, just using si
, and converting on output.
Ok i guess I'm trying to write my code to generally and I should just be using Kelvin
Here's what that example might look like in dimensioned:
use dimensioned::{si, f64prefixes::*};
fn circumference(r: si::Meter<f64>) -> si::Meter<f64> {
2.0 * r * std::f64::consts::PI
}
fn main() {
let l1 = 10.0 * MILLI * si::M;
let l2 = 5.0 * si::M;
let l3 = (5.0 * l1) + l2;
let l3_meters = l3.value_unsafe;
let c1 = circumference(l1);
println!("l1 = {}", l1);
println!("l2 = {}", l2);
println!("l3 = (5 * l1) + l2 = {}", l3);
println!("l3_meters = {}", l3_meters);
println!("circumference(radius = {}) = {}", l1, c1);
println!("l3 > l2 : {}", l3 > l2);
println!("l3 / l2 = {}", l3 / l2);
}
The difference here is that all prints will be in meters, it would be up to you to output in a different unit if desired.
ok thanks!
(I'm sorry. I'm very new to rust and having a hard time understanding the type system.)
What I'm trying to achieve is a method on a struct that accepts any temperature, and uses it in a calculation to return a unitless value.
I'm trying to do something like this:
But I get the error:
I feel like i'm missing something, because i don't actually want to make this THAT generic. I feel like any type that implements
trait Temperature
should be able to be converted into Kelvin... so what gives? How do i do this?Thanks in advance.