Skip to content

refactor(curves,surfaces): eliminate .unwrap()/.expect() (#319)#353

Merged
joaquinbejar merged 5 commits intomainfrom
issue-319-panic-free-surfaces-curves
Apr 17, 2026
Merged

refactor(curves,surfaces): eliminate .unwrap()/.expect() (#319)#353
joaquinbejar merged 5 commits intomainfrom
issue-319-panic-free-surfaces-curves

Conversation

@joaquinbejar
Copy link
Copy Markdown
Owner

Summary

  • Removes every .unwrap() / .expect() in production (non-#[cfg(test)]) code under src/curves/ and src/surfaces/ per the M1 — Panic-Free Core milestone.
  • 50 sites total across src/curves/curve.rs (18), src/surfaces/surface.rs (25), src/curves/traits.rs (2), and 4 doc-comment examples in the visualization modules.
  • No new error variants needed; existing CurveError::AnalysisError / InterpolationError / invalid_parameters and SurfaceError::AnalysisError / invalid_parameters cover every case.
  • No public-API change. Index<usize>::index keeps the trait-mandated panic on out-of-bounds but uses an explicit panic! with index and len instead of .expect("Index out of bounds") (satisfies the panic-free grep, identical user-facing behaviour).

Per-pattern transformations applied

Pattern Replacement
Decimal::partial_cmp(...).unwrap() in sort/min_by/max_by closures unwrap_or(std::cmp::Ordering::Equal) (NaN/Inf guard per global_rules.md §Numerical Discipline)
min_by/max_by(...).unwrap() Option chains in Result-returning fns ok_or_else(|| <Module>Error::AnalysisError(...)) + ?
iter().nth(index).expect("Index out of bounds") in Index::index explicit panic!("...index = N, len = M") + # Panics doc
Decimal::sqrt().unwrap() in Result-returning fns ok_or_else(|| SurfaceError::invalid_parameters("sqrt", ...)) + ?
Decimal::sqrt().unwrap() in non-Result closures (sort comparators) unwrap_or_else(|| { tracing::warn!(...); Decimal::ZERO }) (operands are non-negative; failure path is theoretical overflow)
to_f64().unwrap() ok_or_else(|| CurveError::invalid_parameters("to_f64", ...)) + ?
get_point(x).unwrap() after contains_point() invariant ok_or_else(|| <Module>Error::InterpolationError(...)) + ?
Doc-comment .expect("panic message") examples wrapped in # fn run() -> Result<(), Box<dyn Error>> shim, propagate via ?

Test plan

  • Acceptance grep returns 0 — awk '/#\[cfg\(test\)\]/{exit} {print}' <file> \| grep -E '\.unwrap\(\)\|\.expect\(' across src/curves/**/*.rs and src/surfaces/**/*.rs
  • cargo clippy --all-targets --all-features --workspace -- -D warnings
  • cargo fmt --all --check
  • cargo test --all-features --workspace (3724 lib + 416 integration + plotly)
  • cargo build --release
  • make pre-push (fix + fmt + lint-fix + test + readme + doc)

Public-surface notes

Closes #319

…xamples (#319)

`src/curves/traits.rs` had two `Decimal::to_f64().unwrap()` sites in
the statistical-curve regression helper; convert via `ok_or_else` to
`CurveError::invalid_parameters`.

`src/curves/visualization/{plotters,mod}.rs` and
`src/surfaces/visualization/plotters.rs` doc-comment examples used
`.expect("panic message")` and `.unwrap_or_else(|_| panic!(...))`;
wrap each in a `# fn main() -> Result<(), Box<dyn Error>>` shim and
propagate via `?`.
Eliminate every production .unwrap()/.expect() in src/curves/curve.rs.

- Index<usize>::index keeps the trait-mandated panic on out-of-bounds
  but uses an explicit panic! with a clear "index = N, len = M"
  message instead of .expect("Index out of bounds"); documents in
  the function-level # Panics section.
- Decimal::partial_cmp(...).unwrap() in sort/min_by/max_by closures
  now falls back to Ordering::Equal per global_rules.md §Numerical
  Discipline (NaN guard at f64↔Decimal boundary).
- min_by/max_by Option chains in compute_range_metrics and extrema
  surface MetricsError::BasicError / CurveError::AnalysisError on
  empty inputs instead of panicking.
- merge_axis_interpolate's get_point unwraps inside contains_point
  branches now surface CurveError::InterpolationError; defensively
  guards an invariant that should always hold but no longer panics.
Eliminate every production .unwrap()/.expect() in src/surfaces/surface.rs.

- Index<usize>::index keeps the trait-mandated panic on out-of-bounds
  but uses an explicit panic! with a clear "index = N, len = M"
  message instead of .expect("Index out of bounds"); documents in
  the function-level # Panics section.
- Decimal::partial_cmp(...).unwrap() in sort/min_by/max_by closures
  now falls back to Ordering::Equal per global_rules.md §Numerical
  Discipline.
- min_by/max_by Option chains in extrema and merge_axis_interpolate
  now surface SurfaceError::AnalysisError on empty / missing-point
  invariants instead of panicking.
- Decimal::sqrt().unwrap() in cubic_interpolate weights and
  get_closest_point comparator (which cannot return Result) fall back
  to Decimal::ZERO with a tracing::warn! diagnostic; the squared
  operands are non-negative so the failure path is theoretical
  (overflow only).
- Doc-comment examples in GeometricObject::construct wrapped in a
  fn run() -> Result<(), Box<dyn Error>> shim so they no longer use
  .unwrap().
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 17, 2026

Codecov Report

❌ Patch coverage is 64.00000% with 36 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/surfaces/surface.rs 62.71% 22 Missing ⚠️
src/curves/curve.rs 74.19% 8 Missing ⚠️
src/curves/traits.rs 40.00% 6 Missing ⚠️
Files with missing lines Coverage Δ
src/curves/visualization/plotters.rs 100.00% <ø> (ø)
src/surfaces/visualization/plotters.rs 100.00% <ø> (ø)
src/curves/traits.rs 87.64% <40.00%> (-6.19%) ⬇️
src/curves/curve.rs 95.45% <74.19%> (-1.48%) ⬇️
src/surfaces/surface.rs 86.09% <62.71%> (-1.04%) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR continues the “Panic-Free Core” milestone work by removing .unwrap() / .expect() from production code in the curves and surfaces modules, replacing them with error propagation, safe fallbacks in comparators, and updated doctest patterns.

Changes:

  • Replaces .unwrap() / .expect() in curve/surface implementations with Result-based error propagation (ok_or_else(...)?) and guarded comparator fallbacks.
  • Updates Index<usize> implementations to use explicit panic! messages (preserving Index’s contract) and documents panics.
  • Refactors visualization doctest examples to return Result and use ? instead of panicking.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/surfaces/visualization/plotters.rs Updates doc example to use Result + ? instead of .expect()/panic cleanup.
src/surfaces/surface.rs Removes unwraps in sorting/interpolation/extrema paths; adds explicit indexing panic message; introduces warn!-based fallbacks.
src/curves/visualization/plotters.rs Updates doc example to use Result + ? instead of .expect()/panic cleanup.
src/curves/visualization/mod.rs Updates module-level doc examples to use Result + ?.
src/curves/traits.rs Converts to_f64().unwrap() conversions in regression generation into typed CurveError returns.
src/curves/curve.rs Replaces indexing .expect() with explicit panic!; removes unwraps from metric/extrema helpers via ok_or_else(...)? and guarded comparisons.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/surfaces/surface.rs Outdated
Comment thread src/surfaces/surface.rs Outdated
Comment thread src/curves/traits.rs Outdated
Comment thread src/curves/traits.rs Outdated
…es + clearer to_f64 error

- src/surfaces/surface.rs::cubic_interpolate: when sqrt() returns None
  (Decimal overflow on very large operand), early-return Decimal::ZERO
  weight to drop the point's contribution rather than treating it as
  zero-distance (which would have made it look infinitely close).
- src/surfaces/surface.rs::get_closest_point: compare squared distances
  directly instead of taking sqrt; ordering is monotonic on
  non-negative inputs, so the sqrt is unnecessary and would otherwise
  introduce a fallback that could distort ordering.
- src/curves/traits.rs: reword to_f64 ok_or_else messages — Decimal has
  no NaN/Inf, so the failure mode is out-of-range / non-representable.
@joaquinbejar
Copy link
Copy Markdown
Owner Author

Thanks for the review. All four inline findings are addressed in 2f9925d.

@joaquinbejar joaquinbejar merged commit 00157bc into main Apr 17, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Eliminate .unwrap()/.expect() in src/surfaces/ + src/curves/

2 participants