Skip to content

Commit 3343511

Browse files
authored
Merge pull request #220 from fitzgen/docs
Document bias and behavior when running out of entropy
2 parents d36482c + db1bbd3 commit 3343511

File tree

8 files changed

+55
-6
lines changed

8 files changed

+55
-6
lines changed

src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ pub enum Error {
88
EmptyChoose,
99
/// There was not enough underlying data to fulfill some request for raw
1010
/// bytes.
11+
///
12+
/// Note that outside of [`Unstructured::bytes`][crate::Unstructured::bytes],
13+
/// most APIs do *not* return this error when running out of underlying arbitrary bytes
14+
/// but silently return some default value instead.
1115
NotEnoughData,
1216
/// The input bytes were not of the right format
1317
IncorrectFormat,

src/foreign/core/bool.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{Arbitrary, Result, Unstructured};
22

3+
/// Returns false, not an error, if this `Unstructured` [is empty][Unstructured::is_empty].
34
impl<'a> Arbitrary<'a> for bool {
45
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
56
Ok(<u8 as Arbitrary<'a>>::arbitrary(u)? & 1 == 1)

src/foreign/core/char.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{Arbitrary, Result, Unstructured};
22

3+
/// Returns '\0', not an error, if this `Unstructured` [is empty][Unstructured::is_empty].
34
impl<'a> Arbitrary<'a> for char {
45
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
56
// The highest unicode code point is 0x11_FFFF

src/foreign/core/option.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{size_hint, Arbitrary, MaxRecursionReached, Result, Unstructured};
22

3+
/// Returns `None`, not an error, if this `Unstructured` [is empty][Unstructured::is_empty].
34
impl<'a, A> Arbitrary<'a> for Option<A>
45
where
56
A: Arbitrary<'a>,

src/foreign/core/sync/atomic.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use {
33
core::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize},
44
};
55

6+
/// Returns false, not an error, if this `Unstructured` [is empty][Unstructured::is_empty].
67
impl<'a> Arbitrary<'a> for AtomicBool {
78
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
89
Arbitrary::arbitrary(u).map(Self::new)
@@ -14,6 +15,7 @@ impl<'a> Arbitrary<'a> for AtomicBool {
1415
}
1516
}
1617

18+
/// Returns zero, not an error, if this `Unstructured` [is empty][Unstructured::is_empty].
1719
impl<'a> Arbitrary<'a> for AtomicIsize {
1820
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
1921
Arbitrary::arbitrary(u).map(Self::new)
@@ -25,6 +27,7 @@ impl<'a> Arbitrary<'a> for AtomicIsize {
2527
}
2628
}
2729

30+
/// Returns zero, not an error, if this `Unstructured` [is empty][Unstructured::is_empty].
2831
impl<'a> Arbitrary<'a> for AtomicUsize {
2932
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
3033
Arbitrary::arbitrary(u).map(Self::new)

src/foreign/core/time.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use {
33
core::time::Duration,
44
};
55

6+
/// Returns zero, not an error, if this `Unstructured` [is empty][Unstructured::is_empty].
67
impl<'a> Arbitrary<'a> for Duration {
78
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
89
Ok(Self::new(

src/lib.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,25 @@ pub trait Arbitrary<'a>: Sized {
425425
}
426426
}
427427

428+
#[cfg(test)]
429+
mod test {
430+
use super::*;
431+
432+
#[test]
433+
fn exhausted_entropy() {
434+
let mut u = Unstructured::new(&[]);
435+
assert_eq!(u.arbitrary::<bool>().unwrap(), false);
436+
assert_eq!(u.arbitrary::<u8>().unwrap(), 0);
437+
assert_eq!(u.arbitrary::<usize>().unwrap(), 0);
438+
assert_eq!(u.arbitrary::<f32>().unwrap(), 0.0);
439+
assert_eq!(u.arbitrary::<f64>().unwrap(), 0.0);
440+
assert_eq!(u.arbitrary::<Option<u32>>().unwrap(), None);
441+
assert_eq!(u.int_in_range(4..=100).unwrap(), 4);
442+
assert_eq!(u.choose_index(10).unwrap(), 0);
443+
assert_eq!(u.ratio(5, 7).unwrap(), true);
444+
}
445+
}
446+
428447
/// Multiple conflicting arbitrary attributes are used on the same field:
429448
/// ```compile_fail
430449
/// #[derive(::arbitrary::Arbitrary)]

src/unstructured.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,11 @@ impl<'a> Unstructured<'a> {
273273
/// Do not use this to generate the size of a collection. Use
274274
/// `arbitrary_len` instead.
275275
///
276+
/// The probability distribution of the return value is not necessarily uniform.
277+
///
278+
/// Returns `range.start()`, not an error,
279+
/// if this `Unstructured` [is empty][Unstructured::is_empty].
280+
///
276281
/// # Panics
277282
///
278283
/// Panics if `range.start > range.end`. That is, the given range must be
@@ -377,8 +382,12 @@ impl<'a> Unstructured<'a> {
377382
///
378383
/// This should only be used inside of `Arbitrary` implementations.
379384
///
380-
/// Returns an error if there is not enough underlying data to make a
381-
/// choice or if no choices are provided.
385+
/// The probability distribution of choices is not necessarily uniform.
386+
///
387+
/// Returns the first choice, not an error,
388+
/// if this `Unstructured` [is empty][Unstructured::is_empty].
389+
///
390+
/// Returns an error if no choices are provided.
382391
///
383392
/// # Examples
384393
///
@@ -416,8 +425,12 @@ impl<'a> Unstructured<'a> {
416425
///
417426
/// This should only be used inside of `Arbitrary` implementations.
418427
///
419-
/// Returns an error if there is not enough underlying data to make a
420-
/// choice or if no choices are provided.
428+
/// The probability distribution of choices is not necessarily uniform.
429+
///
430+
/// Returns the first choice, not an error,
431+
/// if this `Unstructured` [is empty][Unstructured::is_empty].
432+
///
433+
/// Returns an error if no choices are provided.
421434
///
422435
/// # Examples
423436
///
@@ -449,6 +462,10 @@ impl<'a> Unstructured<'a> {
449462

450463
/// Choose a value in `0..len`.
451464
///
465+
/// The probability distribution of return values is not necessarily uniform.
466+
///
467+
/// Returns zero, not an error, if this `Unstructured` [is empty][Unstructured::is_empty].
468+
///
452469
/// Returns an error if the `len` is zero.
453470
///
454471
/// # Examples
@@ -492,7 +509,9 @@ impl<'a> Unstructured<'a> {
492509
Ok(idx)
493510
}
494511

495-
/// Generate a boolean according to the given ratio.
512+
/// Generate a boolean which is true with probability approximately the given ratio.
513+
///
514+
/// Returns true, not an error, if this `Unstructured` [is empty][Unstructured::is_empty].
496515
///
497516
/// # Panics
498517
///
@@ -512,7 +531,7 @@ impl<'a> Unstructured<'a> {
512531
/// let mut u = Unstructured::new(&my_data);
513532
///
514533
/// if u.ratio(5, 7)? {
515-
/// // Take this branch 5/7 of the time.
534+
/// // Take this branch approximately 5/7 of the time.
516535
/// }
517536
/// # Ok(())
518537
/// # }

0 commit comments

Comments
 (0)