@@ -310,13 +310,17 @@ pub fn spin_loop() {
310310/// behavior in the calling code. This property makes `black_box` useful for writing code in which
311311/// certain optimizations are not desired, such as benchmarks.
312312///
313+ /// <div class="warning">
314+ ///
313315/// Note however, that `black_box` is only (and can only be) provided on a "best-effort" basis. The
314316/// extent to which it can block optimisations may vary depending upon the platform and code-gen
315317/// backend used. Programs cannot rely on `black_box` for *correctness*, beyond it behaving as the
316318/// identity function. As such, it **must not be relied upon to control critical program behavior.**
317319/// This also means that this function does not offer any guarantees for cryptographic or security
318320/// purposes.
319321///
322+ /// </div>
323+ ///
320324/// [`std::convert::identity`]: crate::convert::identity
321325///
322326/// # When is this useful?
@@ -357,7 +361,7 @@ pub fn spin_loop() {
357361/// ```
358362/// use std::hint::black_box;
359363///
360- /// // Same `contains` function
364+ /// // Same `contains` function.
361365/// fn contains(haystack: &[&str], needle: &str) -> bool {
362366/// haystack.iter().any(|x| x == &needle)
363367/// }
@@ -366,8 +370,13 @@ pub fn spin_loop() {
366370/// let haystack = vec!["abc", "def", "ghi", "jkl", "mno"];
367371/// let needle = "ghi";
368372/// for _ in 0..10 {
369- /// // Adjust our benchmark loop contents
370- /// black_box(contains(black_box(&haystack), black_box(needle)));
373+ /// // Force the compiler to run `contains`, even though it is a pure function whose
374+ /// // results are unused.
375+ /// black_box(contains(
376+ /// // Prevent the compiler from making assumptions about the input.
377+ /// black_box(&haystack),
378+ /// black_box(needle),
379+ /// ));
371380/// }
372381/// }
373382/// ```
@@ -382,6 +391,83 @@ pub fn spin_loop() {
382391///
383392/// This makes our benchmark much more realistic to how the function would actually be used, where
384393/// arguments are usually not known at compile time and the result is used in some way.
394+ ///
395+ /// # How to use this
396+ ///
397+ /// In practice, `black_box` serves two purposes:
398+ ///
399+ /// 1. It prevents the compiler from making optimizations related to the value returned by `black_box`
400+ /// 2. It forces the value passed to `black_box` to be calculated, even if the return value of `black_box` is unused
401+ ///
402+ /// ```
403+ /// use std::hint::black_box;
404+ ///
405+ /// let zero = 0;
406+ /// let five = 5;
407+ ///
408+ /// // The compiler will see this and remove the `* five` call, because it knows that multiplying
409+ /// // any integer by 0 will result in 0.
410+ /// let c = zero * five;
411+ ///
412+ /// // Adding `black_box` here disables the compiler's ability to reason about the first operand in the multiplication.
413+ /// // It is forced to assume that it can be any possible number, so it cannot remove the `* five`
414+ /// // operation.
415+ /// let c = black_box(zero) * five;
416+ /// ```
417+ ///
418+ /// While most cases will not be as clear-cut as the above example, it still illustrates how
419+ /// `black_box` can be used. When benchmarking a function, you usually want to wrap its inputs in
420+ /// `black_box` so the compiler cannot make optimizations that would be unrealistic in real-life
421+ /// use.
422+ ///
423+ /// ```
424+ /// use std::hint::black_box;
425+ ///
426+ /// // This is a simple function that increments its input by 1. Note that it is pure, meaning it
427+ /// // has no side-effects. This function has no effect if its result is unused. (An example of a
428+ /// // function *with* side-effects is `println!()`.)
429+ /// fn increment(x: u8) -> u8 {
430+ /// x + 1
431+ /// }
432+ ///
433+ /// // Here, we call `increment` but discard its result. The compiler, seeing this and knowing that
434+ /// // `increment` is pure, will eliminate this function call entirely. This may not be desired,
435+ /// // though, especially if we're trying to track how much time `increment` takes to execute.
436+ /// let _ = increment(black_box(5));
437+ ///
438+ /// // Here, we force `increment` to be executed. This is because the compiler treats `black_box`
439+ /// // as if it has side-effects, and thus must compute its input.
440+ /// let _ = black_box(increment(black_box(5)));
441+ /// ```
442+ ///
443+ /// There may be additional situations where you want to wrap the result of a function in
444+ /// `black_box` to force its execution. This is situational though, and may not have any effect
445+ /// (such as when the function returns a zero-sized type such as [`()` unit][unit]).
446+ ///
447+ /// Note that `black_box` has no effect on how its input is treated, only its output. As such,
448+ /// expressions passed to `black_box` may still be optimized:
449+ ///
450+ /// ```
451+ /// use std::hint::black_box;
452+ ///
453+ /// // The compiler sees this...
454+ /// let y = black_box(5 * 10);
455+ ///
456+ /// // ...as this. As such, it will likely simplify `5 * 10` to just `50`.
457+ /// let _0 = 5 * 10;
458+ /// let y = black_box(_0);
459+ /// ```
460+ ///
461+ /// In the above example, the `5 * 10` expression is considered distinct from the `black_box` call,
462+ /// and thus is still optimized by the compiler. You can prevent this by moving the multiplication
463+ /// operation outside of `black_box`:
464+ ///
465+ /// ```
466+ /// use std::hint::black_box;
467+ ///
468+ /// // No assumptions can be made about either operand, so the multiplication is not optimized out.
469+ /// let y = black_box(5) * black_box(10);
470+ /// ```
385471#[ inline]
386472#[ stable( feature = "bench_black_box" , since = "1.66.0" ) ]
387473#[ rustc_const_unstable( feature = "const_black_box" , issue = "none" ) ]
0 commit comments