Closed JeromeSchmied closed 3 weeks ago
Floats are not reals, nor even decimals, they are binary numbers, so this is expected behavior. Sorry.
You can also reveal this quirk by writing
assert_eq!(0.3_f64, 0.2 + 0.1);
thanks, than I suppose it's solved
but a quick question before closing, how can I test this kind of stuff?
I tried this code:
full code
```rust //! A performant [srtm](https://www.earthdata.nasa.gov/sensors/srtm) reader for `.hgt` files. //! //! # Usage //! //! ```rust //! use srtm_reader::Tile; //! use std::path::PathBuf; //! //! // the actual elevation of Veli Brig, 263m //! const TRUE_ELEV: i16 = 263; //! // the coordinates of Veli Brig, actual elevation: 263m //! let coord = (44.4480403, 15.0733053); //! // we get the filename, that shall include the elevation data for this `coord` //! let filename = srtm_reader::get_filename(coord); //! // in this case, the filename will be: //! assert_eq!(filename, "N44E015.hgt"); //! // load the srtm tile: .hgt file //! let tile = Tile::from_file(filename).unwrap(); //! // and finally, retrieve our elevation for Veli Brig //! let elevation = tile.get(coord); //! // test with a ± 5m //! assert!((TRUE_ELEV - 5..TRUE_ELEV + 5).contains(&elevation)); //! println!("Veli Brig:\n\t- coordinates: {coord:?}\n\t- elevation\n\t\t- actual: {TRUE_ELEV}m\n\t\t- calculated: {elevation}m"); //! ``` use byteorder::{BigEndian, ReadBytesExt}; use std::fs::{self, File}; use std::io::{self, BufReader, Read}; use std::path::Path; /// this many rows and columns are there in a standard SRTM1 file const EXTENT: usize = 3600; /// the available resulutions of the SRTM data, in arc seconds #[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Debug, Default)] pub enum Resolution { SRTM05, #[default] SRTM1, SRTM3, } impl Resolution { /// the number of rows and columns in an SRTM data file of [`Resolution`] pub const fn extent(&self) -> usize { match self { Resolution::SRTM05 => EXTENT * 2 + 1, Resolution::SRTM1 => EXTENT + 1, Resolution::SRTM3 => EXTENT / 3 + 1, } } /// total file length in BigEndian, total file length in bytes is [`Resolution::total_len()`] * 2 pub const fn total_len(&self) -> usize { self.extent().pow(2) } } /// the SRTM tile, which contains the actual elevation data #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct Tile { pub latitude: i32, pub longitude: i32, pub resolution: Resolution, pub data: Vec,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
ParseLatLong,
Filesize,
Read,
}
/// coordinates
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Default)]
pub struct Coord {
/// latitude: north-south
lat: f64,
/// longitude: east-west
lon: f64,
}
impl Coord {
pub fn new(lat: impl Into, lon: impl Into) -> Self {
let lat = lat.into();
let lon = lon.into();
assert!((-90. ..=90.).contains(&lat));
assert!((-180. ..=180.).contains(&lon));
Self { lat, lon }
}
pub fn with_lat(self, lat: impl Into) -> Self {
Self::new(lat, self.lon)
}
pub fn with_lon(self, lon: impl Into) -> Self {
Self::new(self.lat, lon)
}
pub fn add_to_lat(self, lat: impl Into) -> Self {
self.with_lat(self.lat + lat.into())
}
pub fn add_to_lon(self, lon: impl Into) -> Self {
self.with_lon(self.lon + lon.into())
}
/// truncate both latitude and longitude
pub fn trunc(&self) -> (i32, i32) {
(self.lat.trunc() as i32, self.lon.trunc() as i32)
}
}
impl, F2: Into> From<(F1, F2)> for Coord {
fn from(value: (F1, F2)) -> Self {
let (lat, lon) = (value.0.into(), value.1.into());
Coord { lat, lon }
}
}
impl Tile {
fn new_empty(lat: i32, lon: i32, res: Resolution) -> Tile {
Tile {
latitude: lat,
longitude: lon,
resolution: res,
data: Vec::new(),
}
}
/// read an srtm: `.hgt` file, and create a [`Tile`] if possible
pub fn from_file>(path: P) -> Result {
let (lat, lon) = get_lat_long(&path)?;
let res = get_resolution(&path).ok_or(Error::Filesize)?;
// eprintln!("resolution: {res:?}");
let file = File::open(&path).map_err(|_| Error::Read)?;
// eprintln!("file: {file:?}");
let reader = BufReader::new(file);
let mut tile = Tile::new_empty(lat, lon, res);
tile.data = parse(reader, tile.resolution).map_err(|e| {
eprintln!("parse error: {e:#?}");
Error::Read
})?;
Ok(tile)
}
/// the maximum height that this [`Tile`] contains
pub fn max_height(&self) -> i16 {
*(self.data.iter().max().unwrap())
}
/// get lower-left corner's latitude and longitude
/// it's needed for [`Tile::get_offset()`]
fn get_origin(&self, coord: Coord) -> Coord {
let lat = coord.lat.trunc() + 1.; // The latitude of the lower-left corner of the tile
let lon = coord.lon.trunc(); // The longitude of the lower-left corner of the tile
Coord { lat, lon }
}
/// calculate where this `coord` is located in this [`Tile`]
fn get_offset(&self, coord: Coord) -> (usize, usize) {
let origin = self.get_origin(coord);
// eprintln!("origin: ({}, {})", origin.0, origin.1);
let extent = self.resolution.extent() as f64;
let row = ((origin.lat - coord.lat) * extent) as usize;
let col = ((coord.lon - origin.lon) * extent) as usize;
(row, col)
}
/// get the elevation of this `coord` from this [`Tile`]
///
/// # Panics
/// If this [`Tile`] doesn't contain `coord`'s elevation
/// *NOTE*: shouldn't happen if [`get_filename()`] was used
pub fn get>(&self, coord: C) -> i16 {
let coord: Coord = coord.into();
let offset = self.get_offset(coord);
let lat = coord.lat.trunc() as i32;
let lon = coord.lon.trunc() as i32;
assert!(
self.latitude <= lat,
"hgt lat: {}, coord lat: {lat}",
self.latitude
);
assert!(
self.longitude <= lon,
"hgt lon: {}, coord lon: {lon}",
self.longitude
);
// eprintln!("offset: ({}, {})", offset.1, offset.0);
self.get_at_offset(offset.1, offset.0)
}
fn get_at_offset(&self, x: usize, y: usize) -> i16 {
self.data[self.idx(x, y)]
}
fn idx(&self, x: usize, y: usize) -> usize {
assert!(x < self.resolution.extent() && y < self.resolution.extent());
y * self.resolution.extent() + x
}
}
/// guess the resolution of the file at `path`
fn get_resolution>(path: P) -> Option {
let from_metadata = |m: fs::Metadata| {
let len = m.len() as usize;
// eprintln!("len: {len}");
if len == Resolution::SRTM05.total_len() * 2 {
Some(Resolution::SRTM05)
} else if len == Resolution::SRTM1.total_len() * 2 {
Some(Resolution::SRTM1)
} else if len == Resolution::SRTM3.total_len() * 2 {
Some(Resolution::SRTM3)
} else {
eprintln!("unknown filesize: {}", len);
None
}
};
fs::metadata(path)
.inspect_err(|e| eprintln!("error: {e:#?}"))
.ok()
.and_then(from_metadata)
}
// FIXME: Better error handling.
fn get_lat_long>(path: P) -> Result<(i32, i32), Error> {
let stem = path.as_ref().file_stem().ok_or(Error::ParseLatLong)?;
let desc = stem.to_str().ok_or(Error::ParseLatLong)?;
if desc.len() != 7 {
return Err(Error::ParseLatLong);
}
let get_char = |n| desc.chars().nth(n).ok_or(Error::ParseLatLong);
let lat_sign = if get_char(0)? == 'N' { 1 } else { -1 };
let lat: i32 = desc[1..3].parse().map_err(|_| Error::ParseLatLong)?;
let lon_sign = if get_char(3)? == 'E' { 1 } else { -1 };
let lon: i32 = desc[4..7].parse().map_err(|_| Error::ParseLatLong)?;
Ok((lat_sign * lat, lon_sign * lon))
}
/// get the name of the file, which shall include this `coord`s elevation
///
/// # Usage
///
/// ```rust
/// // the `coord`inate, whe want the elevation for
/// let coord = (87.235, 10.4234423);
/// // this convenient function gives us the filename for
/// // any `coord`inate, that is `impl Into`
/// // which is true for this tuple
/// let filename = srtm_reader::get_filename(coord);
/// assert_eq!(filename, "N87E010.hgt");
/// ```
pub fn get_filename>(coord: C) -> String {
let coord: Coord = coord.into();
let lat_ch = if coord.lat >= 0. { 'N' } else { 'S' };
let lon_ch = if coord.lon >= 0. { 'E' } else { 'W' };
let lat = (coord.lat.trunc() as i32).abs();
let lon = (coord.lon.trunc() as i32).abs();
format!(
"{lat_ch}{}{lat}{lon_ch}{}{lon}.hgt",
if lat < 10 { "0" } else { "" },
if lon < 10 {
"00"
} else if lon < 100 {
"0"
} else {
""
}
)
}
fn parse(reader: R, res: Resolution) -> io::Result> {
let mut reader = reader;
let mut data = Vec::new();
// eprintln!("total size: {}", res.total_size());
for _ in 0..res.total_len() {
// eprint!("{i} ");
let h = reader.read_i16::()?;
data.push(h);
}
Ok(data)
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::Path;
#[test]
fn parse_latitute_and_longitude() {
let ne = Path::new("N35E138.hgt");
assert_eq!(get_lat_long(ne).unwrap(), (35, 138));
let nw = Path::new("N35W138.hgt");
assert_eq!(get_lat_long(nw).unwrap(), (35, -138));
let se = Path::new("S35E138.hgt");
assert_eq!(get_lat_long(se).unwrap(), (-35, 138));
let sw = Path::new("S35W138.hgt");
assert_eq!(get_lat_long(sw).unwrap(), (-35, -138));
}
#[test]
fn total_file_sizes() {
assert_eq!(103_708_802 / 2, Resolution::SRTM05.total_len());
assert_eq!(25_934_402 / 2, Resolution::SRTM1.total_len());
assert_eq!(2_884_802 / 2, Resolution::SRTM3.total_len());
}
#[test]
fn extents() {
assert_eq!(7201, Resolution::SRTM05.extent());
assert_eq!(3601, Resolution::SRTM1.extent());
assert_eq!(1201, Resolution::SRTM3.extent());
}
#[test]
#[should_panic]
fn wrong_coord_0() {
let _ = Coord::new(-190, 42.4);
}
#[test]
#[should_panic]
fn wrong_coord_1() {
let _ = Coord::new(180, -42.4);
}
#[test]
#[should_panic]
fn wrong_coord_2() {
let _ = Coord::new(-90., 181.);
}
#[test]
#[should_panic]
fn wrong_coord_3() {
let _ = Coord::new(90., -180.00001);
}
#[test]
fn correct_coord_0() {
let _ = Coord::new(-90, 180);
}
#[test]
fn correct_coord_1() {
let _ = Coord::new(90, -180);
}
#[test]
fn correct_coord_2() {
let c = Coord::new(90, -180).with_lon(-85.7);
assert_eq!(Coord::new(90, -85.7), c);
}
#[test]
fn correct_coord_3() {
let c = Coord::new(90, -180).with_lat(0.3);
assert_eq!(Coord::new(0.3, -180), c);
}
#[test]
fn correct_coord_4() {
let c = Coord::new(90, -180).with_lat(0.3).with_lon(83.3);
assert_eq!(Coord::new(0.3, 83.3), c);
}
#[test]
fn correct_coord_5() {
let c: Coord = (90, -180).into();
let c = c.with_lat(0.3).with_lon(83.3);
assert_eq!(Coord::new(0.3, 83.3), c);
}
#[test]
fn correct_coord_6() {
let c: Coord = (90, -180).into();
let c = c.with_lat(0.3).with_lon(83.3);
assert_eq!(Coord::new(0.3, 83.3), c);
}
#[test]
fn correct_coord_7() {
let c: Coord = (90, -180.).into();
assert_eq!(Coord::new(90, -180), c);
let c = c.add_to_lat(-20.3252);
assert_eq!(Coord::new(69.6748, -180), c);
let c = c.add_to_lon(35.342);
assert_eq!(Coord::new(69.6748, -144.658), c);
}
#[test]
fn correct_coord_8() {
let c: Coord = (-90, 180).into();
let c = c.add_to_lat(0.3252).add_to_lon(-3.2);
assert_eq!(Coord::new(-89.6748, 176.8), c);
}
fn coords() -> [Coord; 3] {
[(45, 1.4).into(), (-2.3, 87).into(), (35, -7).into()]
}
#[test]
fn file_names() {
let fnames = coords()
.iter()
.map(|c| get_filename(*c))
.collect::>();
assert_eq!(fnames[0], "N45E001.hgt");
assert_eq!(fnames[1], "S02E087.hgt");
assert_eq!(fnames[2], "N35W007.hgt");
}
#[test]
fn read() {
let coord = Coord::new(44.4480403, 15.0733053);
let fname = get_filename(coord);
let tile = Tile::from_file(fname).unwrap();
assert_eq!(tile.latitude, 44);
assert_eq!(tile.longitude, 15);
assert_eq!(tile.resolution, Resolution::SRTM1);
assert_eq!(tile.data.len(), Resolution::SRTM1.total_len());
let elev = tile.get(coord);
assert_eq!(elev, 258);
}
}
```
I expected to see this happen:
cargo test
without errorsInstead, this happened:
Meta
rustc --version --verbose
:Backtrace
``` RUST_BACKTRACE=full cargo t Finished `test` profile [unoptimized + debuginfo] target(s) in 0.00s Running unittests src/lib.rs (target/debug/deps/srtm_reader-6f9846fbd3702ba2) running 18 tests test tests::correct_coord_2 ... ok test tests::correct_coord_3 ... ok test tests::correct_coord_5 ... ok test tests::correct_coord_1 ... ok test tests::correct_coord_4 ... ok test tests::correct_coord_0 ... ok test tests::correct_coord_6 ... ok test tests::correct_coord_8 ... ok test tests::extents ... ok test tests::file_names ... ok test tests::parse_latitute_and_longitude ... ok test tests::total_file_sizes ... ok test tests::correct_coord_7 ... FAILED test tests::wrong_coord_0 - should panic ... ok test tests::wrong_coord_1 - should panic ... ok test tests::wrong_coord_2 - should panic ... ok test tests::wrong_coord_3 - should panic ... ok test tests::read ... ok failures: ---- tests::correct_coord_7 stdout ---- thread 'tests::correct_coord_7' panicked at src/lib.rs:371:9: assertion `left == right` failed left: Coord { lat: 69.6748, lon: -144.658 } right: Coord { lat: 69.6748, lon: -144.65800000000002 } stack backtrace: 0: 0x5dc7dc088955 - std::backtrace_rs::backtrace::libunwind::trace::h649ab3318d3445c5 at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/../../backtrace/src/backtrace/libunwind.rs:116:5 1: 0x5dc7dc088955 - std::backtrace_rs::backtrace::trace_unsynchronized::hf4bb60c3387150c3 at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5 2: 0x5dc7dc088955 - std::sys::backtrace::_print_fmt::hd9186c800e44bd00 at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/sys/backtrace.rs:65:5 3: 0x5dc7dc088955 -::fmt::h1b9dad2a88e955ff
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/sys/backtrace.rs:40:26
4: 0x5dc7dc0af90b - core::fmt::rt::Argument::fmt::h351a7824f737a6a0
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/fmt/rt.rs:173:76
5: 0x5dc7dc0af90b - core::fmt::write::h4b5a1270214bc4a7
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/fmt/mod.rs:1182:21
6: 0x5dc7dc085c3f - std::io::Write::write_fmt::h9d1e399061051a36
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/io/mod.rs:1827:15
7: 0x5dc7dc08a171 - std::sys::backtrace::BacktraceLock::print::h68d41b51481bce5c
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/sys/backtrace.rs:43:9
8: 0x5dc7dc08a171 - std::panicking::default_hook::{{closure}}::h96ab15e9936be7ed
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panicking.rs:269:22
9: 0x5dc7dc089d18 - std::panicking::default_hook::h3cacb9c27561ad33
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panicking.rs:293:9
10: 0x5dc7dc05603a - as core::ops::function::Fn>::call::hd212b1446b2b2077
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/alloc/src/boxed.rs:2084:9
11: 0x5dc7dc05603a - test::test_main::{{closure}}::hd15ff34f3f68988b
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/test/src/lib.rs:136:21
12: 0x5dc7dc08aa7f - as core::ops::function::Fn>::call::hce7569f4ca5d1b64
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/alloc/src/boxed.rs:2084:9
13: 0x5dc7dc08aa7f - std::panicking::rust_panic_with_hook::hfe205f6954b2c97b
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panicking.rs:808:13
14: 0x5dc7dc08a6a7 - std::panicking::begin_panic_handler::{{closure}}::h6cb44b3a50f28c44
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panicking.rs:674:13
15: 0x5dc7dc088e19 - std::sys::backtrace::__rust_end_short_backtrace::hf1c1f2a92799bb0e
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/sys/backtrace.rs:168:18
16: 0x5dc7dc08a334 - rust_begin_unwind
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panicking.rs:665:5
17: 0x5dc7dc0ae073 - core::panicking::panic_fmt::h3d8fc78294164da7
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/panicking.rs:74:14
18: 0x5dc7dc0ae35e - core::panicking::assert_failed_inner::h19a8ffbd06abbe27
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/panicking.rs:410:17
19: 0x5dc7dc01380e - core::panicking::assert_failed::hac521a992a1b890c
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/panicking.rs:365:5
20: 0x5dc7dc01d089 - srtm_reader::tests::correct_coord_7::h08800787e54c2971
at /home/jero/code/rust/projects/garmin/srtm_reader/src/lib.rs:371:9
21: 0x5dc7dc01ac87 - srtm_reader::tests::correct_coord_7::{{closure}}::hb481e2337a57507e
at /home/jero/code/rust/projects/garmin/srtm_reader/src/lib.rs:365:25
22: 0x5dc7dc015f16 - core::ops::function::FnOnce::call_once::h92dd2f6e95fff18b
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/ops/function.rs:250:5
23: 0x5dc7dc05a67b - core::ops::function::FnOnce::call_once::h81f56a195fe4862e
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/ops/function.rs:250:5
24: 0x5dc7dc05a67b - test::__rust_begin_short_backtrace::h919c79c8b896f9e2
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/test/src/lib.rs:624:18
25: 0x5dc7dc059f25 - test::run_test_in_process::{{closure}}::h7b3d5751c5b4dd75
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/test/src/lib.rs:647:60
26: 0x5dc7dc059f25 - as core::ops::function::FnOnce<()>>::call_once::hdabd61465e4dbd80
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/panic/unwind_safe.rs:272:9
27: 0x5dc7dc059f25 - std::panicking::try::do_call::hc813c79fd64b0a90
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panicking.rs:557:40
28: 0x5dc7dc059f25 - std::panicking::try::h055c5de7e7bfc209
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panicking.rs:521:19
29: 0x5dc7dc059f25 - std::panic::catch_unwind::h4265d6525195c807
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panic.rs:350:14
30: 0x5dc7dc059f25 - test::run_test_in_process::he72c277a35f96567
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/test/src/lib.rs:647:27
31: 0x5dc7dc059f25 - test::run_test::{{closure}}::h974e632522c0fbcf
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/test/src/lib.rs:568:43
32: 0x5dc7dc020ca4 - test::run_test::{{closure}}::hdc2c89ce8b601dda
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/test/src/lib.rs:598:41
33: 0x5dc7dc020ca4 - std::sys::backtrace::__rust_begin_short_backtrace::h342cb8e53aeb2076
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/sys/backtrace.rs:152:18
34: 0x5dc7dc0243d2 - std::thread::Builder::spawn_unchecked_::{{closure}}::{{closure}}::h67b1b5c1709ad95b
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/thread/mod.rs:538:17
35: 0x5dc7dc0243d2 - as core::ops::function::FnOnce<()>>::call_once::hd8c7a030ea8b7676
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/panic/unwind_safe.rs:272:9
36: 0x5dc7dc0243d2 - std::panicking::try::do_call::h512c2ab2c15b7d31
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panicking.rs:557:40
37: 0x5dc7dc0243d2 - std::panicking::try::h5c2903f8937bc868
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panicking.rs:521:19
38: 0x5dc7dc0243d2 - std::panic::catch_unwind::h242c80217c2dbece
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/panic.rs:350:14
39: 0x5dc7dc0243d2 - std::thread::Builder::spawn_unchecked_::{{closure}}::h6cb4494ebdd8caf7
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/thread/mod.rs:537:30
40: 0x5dc7dc0243d2 - core::ops::function::FnOnce::call_once{{vtable.shim}}::h42193b008049ba94
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/core/src/ops/function.rs:250:5
41: 0x5dc7dc08ebab - as core::ops::function::FnOnce>::call_once::ha1963004222e7822
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/alloc/src/boxed.rs:2070:9
42: 0x5dc7dc08ebab - as core::ops::function::FnOnce>::call_once::h1086ced1f7c494c2
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/alloc/src/boxed.rs:2070:9
43: 0x5dc7dc08ebab - std::sys::pal::unix::thread::Thread::new::thread_start::ha8af9c992ef0b208
at /rustc/eeb90cda1969383f56a2637cbd3037bdf598841c/library/std/src/sys/pal/unix/thread.rs:108:17
44: 0x768525b9039d -
45: 0x768525c1549c -
46: 0x0 -
failures:
tests::correct_coord_7
test result: FAILED. 17 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.03s
error: test failed, to rerun pass `--lib`
```