Math in Rust is a little bit different in Rust than other languages you may be familiar with.
For example in Python if you want to compute the sine for an angle of 55 degrees, you would first import
the math
module, convert the angle to radians and then use the
value of the math.sin()
function:
>>> # NOTE: Python example! Not Rust! >>> import math >>> angle = 55 >>> angle_rad = math.radians(angle) >>> math.sin(angle_rad) 0.8191520442889917
In Rust you use the methods of the numeric type you are working with. For example, the f32
type is good for math unless you absolutely need the added precision of the f64
type. So, you could
declare your angle as f32
. Then you would use the to_radians
function of that type
to convert degrees to radians, and finally you would take the sine of that:
fn main() { let angle = 55f32; let angle_rad = angle.to_radians(); println!("{}", angle_rad.sin()); }
0.81915206
You will soon get used to starting with the value.
The Rust Cookbook has an example of computing the distance between two geographic locations using the Haversine formula. The result is accurate for the purpose, as it is rarely possible to follow the shortest path when travelling from one location to another. However, the example uses a fixed value for Earth's radius, but that can vary depending on the latitude.
Here is a function to compute the value of Earth's radius in kilometers at a given latitude. You could pass in the latitude component of either of the geographic coordinates, or perhaps their average. The function also depends on two constants defined as values in kilometers.
const EARTH_EQUATORIAL_RADIUS: f32 = 6378.1370; const EARTH_POLAR_RADIUS: f32 = 6356.7523; // Computes the geocentric radius of the Earth for the given latitude. // See http://en.wikipedia.org/wiki/Earth_radius#Geocentric_radius // See https://rust-lang-nursery.github.io/rust-cookbook/science/mathematics/trigonometry.html fn earth_geocentric_radius(latitude_degrees: f32) -> f32 { let latitude = latitude_degrees.to_radians(); let a = EARTH_EQUATORIAL_RADIUS; let b = EARTH_POLAR_RADIUS; let t1a = a.powi(2) * latitude.cos(); let t1b = b.powi(2) * latitude.sin(); let t1 = t1a.powi(2) + t1b.powi(2); let t2a = a * latitude.cos(); let t2b = b * latitude.sin(); let t2 = t2a.powi(2) + t2b.powi(2); (t1 / t2).sqrt() }
If you replace the value of earth_radius_kilometer
in the Rust Cookbook example
with the value obtained from the earth_geocentric_radius
function above (with the
latitude given as the average of the latitudes of Paris and London), you will find that the result
differs by 0.3 kilometers, which is hardly significant in this case:
Earth's geocentric radius for latitude 50.18097 degrees is 6365.565 kilometers Distance between Paris and London on the surface of Earth is 334.7 kilometers
What is more interesting here is the way that the various mathematical functions are used in Rust. The standard library defines the usual trigonometic functions for the floating-point types, and many more.
Since the formula for computing Earth's geocentric radius requires some values to be squared,
the function uses the powi
function
of the f32
type, which uses an
integer exponent, and should be faster than the powf
function which allows you to use
a floating-point exponent.
Another special feature of Rust worth mentioning is the return value of the function. While Rust
does have a return
statement, the idiomatic way is to let the last expression in the
function act as the return value. Here the expression (t1 / t2).sqrt()
is that return
value.
Finally, here is a complete example that implements the Haversine formula as a function. It gives you another idea of how the Rust mathematical functions are used.
// A coordinate point, with latitude and longitude in degrees. struct Coordinates { latitude: f32, longitude: f32, } // Computes the distance between two geographical points in kilometers. // The coordinate values are expressed in degrees. // Uses the haversine formula (see http://en.wikipedia.org/wiki/Haversine_formula). // This implementation is derived from the JavaScript code // by Andrew Hedges at http://andrew.hedges.name/experiments/haversine/. // The Earth radius defaults to the Earth's equatorial radius. // For added accuracy, you may want pass an Earth radius value more suitable for the // latitudes of the points you are processing. fn haversine(point1: Coordinates, point2: Coordinates, earth_radius: f32) -> f32 { let lat1 = point1.latitude.to_radians(); let lon1 = point1.longitude.to_radians(); let lat2 = point2.latitude.to_radians(); let lon2 = point2.longitude.to_radians(); let dlon = lon2 - lon1; let dlat = lat2 - lat1; let a = ((dlat / 2.0).sin()).powi(2) + lat1.cos() * lat2.cos() * ((dlon / 2.0).sin()).powi(2); let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt()); earth_radius * c } fn main() { let paris = Coordinates { latitude: 48.85341, longitude: -2.34880 }; let london = Coordinates { latitude: 51.50853, longitude: -0.12574 }; let average_latitude = (paris.latitude + london.latitude) / 2.0; let radius = earth_geocentric_radius(average_latitude); println!("Earth's geocentric radius for latitude {} degrees is {} kilometers", average_latitude, earth_geocentric_radius(average_latitude)); let distance = haversine(paris, london, radius); println!("Distance between Paris and London on the surface of Earth is {:.1} kilometers", distance); }