diff --git a/src/study.rs b/src/study.rs index 9dc32f5..82fdacf 100644 --- a/src/study.rs +++ b/src/study.rs @@ -1790,6 +1790,31 @@ where } } +impl Study +where + V: PartialOrd + Clone, +{ + /// Returns an iterator over all completed trials. + /// + /// This clones the internal trial list, so it is suitable for + /// analysis and iteration but not for hot paths. + pub fn iter(&self) -> std::vec::IntoIter> { + self.trials().into_iter() + } +} + +impl IntoIterator for &Study +where + V: PartialOrd + Clone, +{ + type Item = CompletedTrial; + type IntoIter = std::vec::IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + impl fmt::Display for Study where V: PartialOrd + Clone + fmt::Display, diff --git a/tests/integration.rs b/tests/integration.rs index e029531..618af9e 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -2097,3 +2097,47 @@ fn test_retries_with_zero_max_retries_same_as_optimize() { assert_eq!(call_count.get(), 5); assert_eq!(study.n_trials(), 5); } + +// ============================================================================= +// Tests: IntoIterator for &Study +// ============================================================================= + +#[test] +fn test_into_iterator_iterates_all_trials() { + let study: Study = Study::with_sampler(Direction::Minimize, RandomSampler::with_seed(42)); + let x_param = FloatParam::new(0.0, 10.0); + + for _ in 0..5 { + let mut trial = study.create_trial(); + let x = x_param.suggest(&mut trial).unwrap(); + study.complete_trial(trial, x * x); + } + + let mut count = 0; + for trial in &study { + assert_eq!(trial.state, optimizer::TrialState::Complete); + count += 1; + } + assert_eq!(count, 5); +} + +#[test] +fn test_into_iterator_empty_study() { + let study: Study = Study::new(Direction::Minimize); + + let count = (&study).into_iter().count(); + assert_eq!(count, 0); +} + +#[test] +fn test_into_iterator_preserves_insertion_order() { + let study: Study = Study::new(Direction::Minimize); + + for i in 0..3 { + let trial = study.create_trial(); + study.complete_trial(trial, f64::from(i)); + } + + let ids: Vec = (&study).into_iter().map(|t| t.id).collect(); + assert_eq!(ids, vec![0, 1, 2]); +}