From 5c63f4d473eed64146bbcd59f74344f8007cafe9 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Wed, 10 Apr 2019 21:28:02 -0400 Subject: [PATCH 1/3] Prevent other crates from implementing our traits This allows us to freely add new functionality to the traits without breaking changes. --- src/correlation.rs | 4 ++++ src/entropy.rs | 4 ++++ src/histogram/histograms.rs | 4 ++++ src/lib.rs | 27 +++++++++++++++++++++++++++ src/maybe_nan/mod.rs | 4 ++++ src/quantile/interpolate.rs | 7 +++++++ src/quantile/mod.rs | 8 ++++++++ src/sort.rs | 4 ++++ src/summary_statistics/means.rs | 2 ++ src/summary_statistics/mod.rs | 2 ++ 10 files changed, 66 insertions(+) diff --git a/src/correlation.rs b/src/correlation.rs index 200bbc45..254d7b9d 100644 --- a/src/correlation.rs +++ b/src/correlation.rs @@ -119,6 +119,8 @@ where fn pearson_correlation(&self) -> Array2 where A: Float + FromPrimitive; + + private_decl! {} } impl CorrelationExt for ArrayBase @@ -164,6 +166,8 @@ where // element-wise division cov / std_matrix } + + private_impl! {} } #[cfg(test)] diff --git a/src/entropy.rs b/src/entropy.rs index 3841cb02..e32d3d43 100644 --- a/src/entropy.rs +++ b/src/entropy.rs @@ -120,6 +120,8 @@ where where S2: Data, A: Float; + + private_decl! {} } impl EntropyExt for ArrayBase @@ -212,6 +214,8 @@ where let cross_entropy = -temp.sum(); Ok(cross_entropy) } + + private_impl! {} } #[cfg(test)] diff --git a/src/histogram/histograms.rs b/src/histogram/histograms.rs index 4eaeaad4..2cd367ce 100644 --- a/src/histogram/histograms.rs +++ b/src/histogram/histograms.rs @@ -147,6 +147,8 @@ where fn histogram(&self, grid: Grid) -> Histogram where A: Ord; + + private_decl! {} } impl HistogramExt for ArrayBase @@ -161,4 +163,6 @@ where } histogram } + + private_impl! {} } diff --git a/src/lib.rs b/src/lib.rs index 6a7cdf38..aada9db2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,33 @@ pub use quantile::{interpolate, Quantile1dExt, QuantileExt}; pub use sort::Sort1dExt; pub use summary_statistics::SummaryStatisticsExt; +#[macro_use] +mod private { + /// This is a public type in a private module, so it can be included in + /// public APIs, but other crates can't access it. + pub struct PrivateMarker; + + /// Defines an associated function for a trait that is impossible for other + /// crates to implement. This makes it possible to add new associated + /// types/functions/consts/etc. to the trait without breaking changes. + macro_rules! private_decl { + () => { + /// This method makes this trait impossible to implement outside of + /// `ndarray-stats`. + fn __private__(&self) -> crate::private::PrivateMarker; + }; + } + + /// Implements the associated function defined by `private_decl!`. + macro_rules! private_impl { + () => { + fn __private__(&self) -> crate::private::PrivateMarker { + crate::private::PrivateMarker + } + }; + } +} + mod correlation; mod entropy; pub mod errors; diff --git a/src/maybe_nan/mod.rs b/src/maybe_nan/mod.rs index 15adbafd..eba3b2f5 100644 --- a/src/maybe_nan/mod.rs +++ b/src/maybe_nan/mod.rs @@ -289,6 +289,8 @@ where S: DataMut, D: RemoveAxis, F: FnMut(ArrayViewMut1<'a, A::NotNan>) -> B; + + private_decl! {} } impl MaybeNanExt for ArrayBase @@ -365,6 +367,8 @@ where { self.map_axis_mut(axis, |lane| mapping(A::remove_nan_mut(lane))) } + + private_impl! {} } #[cfg(test)] diff --git a/src/quantile/interpolate.rs b/src/quantile/interpolate.rs index a0fe64d4..0b7871d9 100644 --- a/src/quantile/interpolate.rs +++ b/src/quantile/interpolate.rs @@ -44,6 +44,8 @@ pub trait Interpolate { /// or if `None` is provided for the higher value when it's needed. #[doc(hidden)] fn interpolate(lower: Option, higher: Option, q: N64, len: usize) -> T; + + private_decl! {} } /// Select the higher value. @@ -69,6 +71,7 @@ impl Interpolate for Higher { fn interpolate(_lower: Option, higher: Option, _q: N64, _len: usize) -> T { higher.unwrap() } + private_impl! {} } impl Interpolate for Lower { @@ -81,6 +84,7 @@ impl Interpolate for Lower { fn interpolate(lower: Option, _higher: Option, _q: N64, _len: usize) -> T { lower.unwrap() } + private_impl! {} } impl Interpolate for Nearest { @@ -97,6 +101,7 @@ impl Interpolate for Nearest { higher.unwrap() } } + private_impl! {} } impl Interpolate for Midpoint @@ -115,6 +120,7 @@ where let higher = higher.unwrap(); lower.clone() + (higher.clone() - lower.clone()) / denom.clone() } + private_impl! {} } impl Interpolate for Linear @@ -135,4 +141,5 @@ where let higher_f64 = higher.to_f64().unwrap(); lower.clone() + T::from_f64(fraction * (higher_f64 - lower_f64)).unwrap() } + private_impl! {} } diff --git a/src/quantile/mod.rs b/src/quantile/mod.rs index 3926e24f..d718d1fa 100644 --- a/src/quantile/mod.rs +++ b/src/quantile/mod.rs @@ -295,6 +295,8 @@ where A::NotNan: Clone + Ord, S: DataMut, I: Interpolate; + + private_decl! {} } impl QuantileExt for ArrayBase @@ -568,6 +570,8 @@ where }); Ok(quantile) } + + private_impl! {} } /// Quantile methods for 1-D arrays. @@ -635,6 +639,8 @@ where S: DataMut, S2: Data, I: Interpolate; + + private_decl! {} } impl Quantile1dExt for ArrayBase @@ -665,6 +671,8 @@ where { self.quantiles_axis_mut(Axis(0), qs, interpolate) } + + private_impl! {} } pub mod interpolate; diff --git a/src/sort.rs b/src/sort.rs index 5a0b5d3b..4f4bfc41 100644 --- a/src/sort.rs +++ b/src/sort.rs @@ -103,6 +103,8 @@ where where A: Ord + Clone, S: DataMut; + + private_decl! {} } impl Sort1dExt for ArrayBase @@ -183,6 +185,8 @@ where self.swap(0, i - 1); i - 1 } + + private_impl! {} } /// To retrieve multiple indexes from the sorted array in an optimized fashion, diff --git a/src/summary_statistics/means.rs b/src/summary_statistics/means.rs index de65a801..89d4df9d 100644 --- a/src/summary_statistics/means.rs +++ b/src/summary_statistics/means.rs @@ -105,6 +105,8 @@ where } } } + + private_impl! {} } /// Returns a vector containing all moments of the array elements up to diff --git a/src/summary_statistics/mod.rs b/src/summary_statistics/mod.rs index e0a71399..8351ff82 100644 --- a/src/summary_statistics/mod.rs +++ b/src/summary_statistics/mod.rs @@ -140,6 +140,8 @@ where fn central_moments(&self, order: u16) -> Result, EmptyInput> where A: Float + FromPrimitive; + + private_decl! {} } mod means; From df7cbb021eca6bfd27d3fe8a428515d45f9bc0f5 Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 11 Apr 2019 12:02:40 -0400 Subject: [PATCH 2/3] Make PrivateMarker a param instead of return type This still prevents other crates from implementing our traits, but it has the advantage that it also prevents them from calling the `__private__` methods. --- src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index aada9db2..92353098 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,16 +61,14 @@ mod private { () => { /// This method makes this trait impossible to implement outside of /// `ndarray-stats`. - fn __private__(&self) -> crate::private::PrivateMarker; + fn __private__(&self, _: crate::private::PrivateMarker); }; } /// Implements the associated function defined by `private_decl!`. macro_rules! private_impl { () => { - fn __private__(&self) -> crate::private::PrivateMarker { - crate::private::PrivateMarker - } + fn __private__(&self, _: crate::private::PrivateMarker) {} }; } } From 5e9dfbe05588a41ed8f13552b30486eaa430085a Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Thu, 11 Apr 2019 12:12:19 -0400 Subject: [PATCH 3/3] Improve docs of __private__ method --- src/lib.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 92353098..09ade038 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,7 +60,15 @@ mod private { macro_rules! private_decl { () => { /// This method makes this trait impossible to implement outside of - /// `ndarray-stats`. + /// `ndarray-stats` so that we can freely add new methods, etc., to + /// this trait without breaking changes. + /// + /// We don't anticipate any other crates needing to implement this + /// trait, but if you do have such a use-case, please let us know. + /// + /// **Warning** This method is not considered part of the public + /// API, and client code should not rely on it being present. It + /// may be removed in a non-breaking release. fn __private__(&self, _: crate::private::PrivateMarker); }; }