Snap for 11399874 from f78e80a01c96543721f1b092a9ad80034dc40f4d to 24D1-release

Change-Id: Iba4380333331664ca7ea73e54a73156fcbf6a640
diff --git a/config.toml b/.cargo/config.toml
similarity index 100%
rename from config.toml
rename to .cargo/config.toml
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index ce5ec95..dae245d 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "6e5405db2217d37006720c101beb1b91199a3a26"
+    "sha1": "91a15f5eb416cbf97cd8b6d8831263f2c861b859"
   },
   "path_in_vcs": "googletest"
 }
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index d7fe4a6..dc74442 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6,7 +6,7 @@
     host_supported: true,
     crate_name: "googletest",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.10.0",
+    cargo_pkg_version: "0.11.0",
     srcs: ["src/lib.rs"],
     edition: "2021",
     rustlibs: [
diff --git a/Cargo.toml b/Cargo.toml
index 3e898d2..ed9d01c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,9 +11,9 @@
 
 [package]
 edition = "2021"
-rust-version = "1.59.0"
+rust-version = "1.66.0"
 name = "googletest"
-version = "0.10.0"
+version = "0.11.0"
 authors = [
     "Bradford Hovinen <hovinen@google.com>",
     "Bastien Jacot-Guillarmod <bjacotg@google.com>",
@@ -34,24 +34,23 @@
 ]
 license = "Apache-2.0"
 repository = "https://github.com/google/googletest-rust"
-resolver = "1"
 
 [dependencies.anyhow]
 version = "1"
 optional = true
 
 [dependencies.googletest_macro]
-version = "0.10.0"
+version = "0.11.0"
 
 [dependencies.num-traits]
-version = "0.2.15"
+version = "0.2.17"
 
 [dependencies.proptest]
 version = "1.2.0"
 optional = true
 
 [dependencies.regex]
-version = "1.6.0"
+version = "1.7.3"
 
 [dependencies.rustversion]
 version = "1.0.14"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..9fedcfb
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,44 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+[package]
+name = "googletest"
+version = "0.11.0"
+keywords = ["unit", "matcher", "testing", "assertions"]
+categories = ["development-tools", "development-tools::testing"]
+description = "A rich assertion and matcher library inspired by GoogleTest for C++"
+repository = "https://github.com/google/googletest-rust"
+readme = "../README.md"
+license = "Apache-2.0"
+edition = "2021"
+rust-version = "1.66.0"
+authors = [
+  "Bradford Hovinen <hovinen@google.com>",
+  "Bastien Jacot-Guillarmod <bjacotg@google.com>",
+  "Maciej Pietrzak <mpi@google.com>",
+  "Martin Geisler <mgeisler@google.com>",
+]
+
+[dependencies]
+googletest_macro = { path = "../googletest_macro", version = "0.11.0" }
+anyhow = { version = "1", optional = true }
+num-traits = "0.2.17"
+proptest = { version = "1.2.0", optional = true }
+regex = "1.7.3"
+rustversion = "1.0.14"
+
+[dev-dependencies]
+indoc = "2"
+quickcheck = "1.0.3"
+serial_test = "2.0.0"
diff --git a/METADATA b/METADATA
index 6c2060b..86fc93a 100644
--- a/METADATA
+++ b/METADATA
@@ -1,19 +1,24 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update external/rust/crates/googletest
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
+
 name: "googletest"
 description: "A rich assertion and matcher library inspired by GoogleTest for C++"
 third_party {
+  license_type: NOTICE
+  last_upgrade_date {
+    year: 2024
+    month: 2
+    day: 2
+  }
   identifier {
     type: "crates.io"
-    value: "https://crates.io/crates/googletest"
+    value: "https://static.crates.io/crates/googletest/googletest-0.11.0.crate"
+    version: "0.10.0"
   }
   identifier {
     type: "Archive"
     value: "https://static.crates.io/crates/googletest/googletest-0.10.0.crate"
-  }
-  version: "0.10.0"
-  license_type: NOTICE
-  last_upgrade_date {
-    year: 2023
-    month: 9
-    day: 1
+    version: "0.11.0"
   }
 }
diff --git a/README.md b/README.md
index 2a0f918..d442770 100644
--- a/README.md
+++ b/README.md
@@ -25,11 +25,14 @@
  * A new set of assertion macros offering similar functionality to those of
    [GoogleTest](https://google.github.io/googletest/primer.html#assertions).
 
-**The minimum supported Rust version is 1.59**. We recommend using at least
-version 1.66 for the best developer experience.
+**The minimum supported Rust version is 1.66**.
 
 > :warning: The API is not fully stable and may still be changed until we
 > publish version 1.0.
+>
+> Moreover, any items or modules starting with `__` (double underscores) must
+> not be used directly. Those items or modules are only for internal uses and
+> their API may change without a major version update.
 
 ## Assertions and matchers
 
@@ -162,10 +165,10 @@
 
     fn describe(&self, matcher_result: MatcherResult) -> String {
         match matcher_result {
-            MatcherResult::Matches => {
+            MatcherResult::Match => {
                 format!("is equal to {:?} the way I define it", self.expected)
             }
-            MatcherResult::DoesNotMatch => {
+            MatcherResult::NoMatch => {
                 format!("isn't equal to {:?} the way I define it", self.expected)
             }
         }
diff --git a/crate_docs.md b/crate_docs.md
index a00c219..8c8b47c 100644
--- a/crate_docs.md
+++ b/crate_docs.md
@@ -153,38 +153,48 @@
 | [`superset_of`]      | A container containing all elements of the argument.                     |
 | [`unordered_elements_are!`] | A container whose elements the arguments match, in any order.     |
 
+[`all!`]: matchers::all
+[`any!`]: matchers::any
 [`anything`]: matchers::anything
 [`approx_eq`]: matchers::approx_eq
 [`char_count`]: matchers::char_count
 [`container_eq`]: matchers::container_eq
 [`contains`]: matchers::contains
+[`contains_each!`]: matchers::contains_each
 [`contains_regex`]: matchers::contains_regex
 [`contains_substring`]: matchers::contains_substring
 [`displays_as`]: matchers::displays_as
 [`each`]: matchers::each
+[`elements_are!`]: matchers::elements_are
 [`empty`]: matchers::empty
 [`ends_with`]: matchers::ends_with
 [`eq`]: matchers::eq
 [`eq_deref_of`]: matchers::eq_deref_of
 [`err`]: matchers::err
+[`field!`]: matchers::field
 [`ge`]: matchers::ge
 [`gt`]: matchers::gt
 [`has_entry`]: matchers::has_entry
+[`is_contained_in!`]: matchers::is_contained_in
 [`is_nan`]: matchers::is_nan
 [`le`]: matchers::le
 [`len`]: matchers::len
 [`lt`]: matchers::lt
 [`matches_regex`]: matchers::matches_regex
+[`matches_pattern!`]: matchers::matches_pattern
 [`near`]: matchers::near
 [`none`]: matchers::none
 [`not`]: matchers::not
+[`pat!`]: matchers::pat
 [`ok`]: matchers::ok
 [`points_to`]: matchers::points_to
+[`pointwise!`]: matchers::pointwise
 [`predicate`]: matchers::predicate
 [`some`]: matchers::some
 [`starts_with`]: matchers::starts_with
 [`subset_of`]: matchers::subset_of
 [`superset_of`]: matchers::superset_of
+[`unordered_elements_are!`]: matchers::unordered_elements_are
 [`Deref`]: std::ops::Deref
 [`Display`]: std::fmt::Display
 [`HashMap`]: std::collections::HashMap
@@ -199,7 +209,7 @@
 [`Matcher`]:
 
 ```no_run
-use googletest::matcher::{Matcher, MatcherResult};
+use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
 use std::fmt::Debug;
 
 struct MyEqMatcher<T> {
@@ -217,13 +227,13 @@
         }
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
             MatcherResult::Match => {
-                format!("is equal to {:?} the way I define it", self.expected)
+                format!("is equal to {:?} the way I define it", self.expected).into()
             }
             MatcherResult::NoMatch => {
-                format!("isn't equal to {:?} the way I define it", self.expected)
+                format!("isn't equal to {:?} the way I define it", self.expected).into()
             }
         }
     }
@@ -233,7 +243,7 @@
  It is recommended to expose a function which constructs the matcher:
 
  ```no_run
- # use googletest::matcher::{Matcher, MatcherResult};
+ # use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
  # use std::fmt::Debug;
  #
  # struct MyEqMatcher<T> {
@@ -251,13 +261,13 @@
  #        }
  #    }
  #
- #    fn describe(&self, matcher_result: MatcherResult) -> String {
+ #    fn describe(&self, matcher_result: MatcherResult) -> Description {
  #        match matcher_result {
  #            MatcherResult::Match => {
- #                format!("is equal to {:?} the way I define it", self.expected)
+ #                format!("is equal to {:?} the way I define it", self.expected).into()
  #            }
  #            MatcherResult::NoMatch => {
- #                format!("isn't equal to {:?} the way I define it", self.expected)
+ #                format!("isn't equal to {:?} the way I define it", self.expected).into()
  #            }
  #        }
  #    }
@@ -272,7 +282,7 @@
 
 ```
 # use googletest::prelude::*;
-# use googletest::matcher::{Matcher, MatcherResult};
+# use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
 # use std::fmt::Debug;
 #
 # struct MyEqMatcher<T> {
@@ -290,13 +300,13 @@
 #        }
 #    }
 #
-#    fn describe(&self, matcher_result: MatcherResult) -> String {
+#    fn describe(&self, matcher_result: MatcherResult) -> Description {
 #        match matcher_result {
 #            MatcherResult::Match => {
-#                format!("is equal to {:?} the way I define it", self.expected)
+#                format!("is equal to {:?} the way I define it", self.expected).into()
 #            }
 #            MatcherResult::NoMatch => {
-#                format!("isn't equal to {:?} the way I define it", self.expected)
+#                format!("isn't equal to {:?} the way I define it", self.expected).into()
 #            }
 #        }
 #    }
diff --git a/src/assertions.rs b/src/assertions.rs
index 7664486..2668028 100644
--- a/src/assertions.rs
+++ b/src/assertions.rs
@@ -120,23 +120,23 @@
 /// not supported; see [Rust by Example](https://doc.rust-lang.org/rust-by-example/primitives/tuples.html#tuples).
 #[macro_export]
 macro_rules! verify_that {
-    ($actual:expr, [$($expecteds:expr),+]) => {
+    ($actual:expr, [$($expecteds:expr),+ $(,)?]) => {
         $crate::assertions::internal::check_matcher(
             &$actual,
-            $crate::elements_are![$($expecteds),+],
+            $crate::matchers::elements_are![$($expecteds),+],
             stringify!($actual),
             $crate::internal::source_location::SourceLocation::new(file!(), line!(), column!()),
         )
     };
-    ($actual:expr, {$($expecteds:expr),+}) => {
+    ($actual:expr, {$($expecteds:expr),+ $(,)?}) => {
         $crate::assertions::internal::check_matcher(
             &$actual,
-            $crate::unordered_elements_are![$($expecteds),+],
+            $crate::matchers::unordered_elements_are![$($expecteds),+],
             stringify!($actual),
             $crate::internal::source_location::SourceLocation::new(file!(), line!(), column!()),
         )
     };
-    ($actual:expr, $expected:expr) => {
+    ($actual:expr, $expected:expr $(,)?) => {
         $crate::assertions::internal::check_matcher(
             &$actual,
             $expected,
@@ -309,16 +309,49 @@
 /// Matches the given value against the given matcher, panicking if it does not
 /// match.
 ///
+/// ```should_panic
+/// # use googletest::prelude::*;
+/// # fn should_fail() {
+/// let value = 2;
+/// assert_that!(value, eq(3));  // Fails and panics.
+/// # }
+/// # should_fail();
+/// ```
+///
 /// This is analogous to assertions in most Rust test libraries, where a failed
 /// assertion causes a panic.
 ///
+/// One may optionally add arguments which will be formatted and appended to a
+/// failure message. For example:
+///
+/// ```should_panic
+/// # use googletest::prelude::*;
+/// # fn should_fail() {
+/// let value = 2;
+/// let extra_information = "Some additional information";
+/// assert_that!(value, eq(3), "Test failed. Extra information: {extra_information}.");
+/// # }
+/// # should_fail();
+/// ```
+///
+/// This is output as follows:
+///
+/// ```text
+/// Value of: value
+/// Expected: is equal to 3
+/// Actual: 2,
+///   which isn't equal to 3
+///   at ...
+/// Test failed. Extra information: Some additional information.
+/// ```
+///
 /// **Note for users of [GoogleTest for C++](http://google.github.io/googletest/):**
 /// This differs from the `ASSERT_THAT` macro in that it panics rather
 /// than triggering an early return from the invoking function. To get behaviour
 /// equivalent to `ASSERT_THAT`, use [`verify_that!`] with the `?` operator.
 #[macro_export]
 macro_rules! assert_that {
-    ($actual:expr, $expected:expr) => {
+    ($actual:expr, $expected:expr $(,)?) => {
         match $crate::verify_that!($actual, $expected) {
             Ok(_) => {}
             Err(e) => {
@@ -328,6 +361,19 @@
             }
         }
     };
+
+    ($actual:expr, $expected:expr, $($format_args:expr),* $(,)?) => {
+        match $crate::verify_that!($actual, $expected)
+            .with_failure_message(|| format!($($format_args),*))
+        {
+            Ok(_) => {}
+            Err(e) => {
+                // The extra newline before the assertion failure message makes the failure a
+                // bit easier to read when there's some generic boilerplate from the panic.
+                panic!("\n{}", e);
+            }
+        }
+    };
 }
 
 /// Asserts that the given predicate applied to the given arguments returns
@@ -368,12 +414,44 @@
 /// ```ignore
 /// verify_that!(actual, expected).and_log_failure()
 /// ```
+///
+/// One may optionally add arguments which will be formatted and appended to a
+/// failure message. For example:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// # fn should_fail() -> std::result::Result<(), googletest::internal::test_outcome::TestFailure> {
+/// # googletest::internal::test_outcome::TestOutcome::init_current_test_outcome();
+/// let value = 2;
+/// let extra_information = "Some additional information";
+/// expect_that!(value, eq(3), "Test failed. Extra information: {extra_information}.");
+/// # googletest::internal::test_outcome::TestOutcome::close_current_test_outcome::<&str>(Ok(()))
+/// # }
+/// # should_fail().unwrap_err();
+/// ```
+///
+/// This is output as follows:
+///
+/// ```text
+/// Value of: value
+/// Expected: is equal to 3
+/// Actual: 2,
+///   which isn't equal to 3
+///   at ...
+/// Test failed. Extra information: Some additional information.
+/// ```
 #[macro_export]
 macro_rules! expect_that {
-    ($actual:expr, $expected:expr) => {{
+    ($actual:expr, $expected:expr $(,)?) => {{
         use $crate::GoogleTestSupport;
         $crate::verify_that!($actual, $expected).and_log_failure();
     }};
+
+    ($actual:expr, $expected:expr, $($format_args:expr),* $(,)?) => {
+        $crate::verify_that!($actual, $expected)
+            .with_failure_message(|| format!($($format_args),*))
+            .and_log_failure()
+    };
 }
 
 /// Asserts that the given predicate applied to the given arguments returns
diff --git a/src/description.rs b/src/description.rs
new file mode 100644
index 0000000..9605559
--- /dev/null
+++ b/src/description.rs
@@ -0,0 +1,362 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::{
+    borrow::Cow,
+    fmt::{Display, Formatter, Result},
+};
+
+use crate::internal::description_renderer::{List, INDENTATION_SIZE};
+
+/// A structured description, either of a (composed) matcher or of an
+/// assertion failure.
+///
+/// One can compose blocks of text into a `Description`. Each one appears on a
+/// new line. For example:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// # use googletest::description::Description;
+/// let description = Description::new()
+///     .text("A block")
+///     .text("Another block");
+/// verify_that!(description, displays_as(eq("A block\nAnother block")))
+/// # .unwrap();
+/// ```
+///
+/// One can embed nested descriptions into a `Description`. The resulting
+/// nested description is then rendered with an additional level of
+/// indentation. For example:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// # use googletest::description::Description;
+/// let inner_description = Description::new()
+///     .text("A block")
+///     .text("Another block");
+/// let outer_description = Description::new()
+///     .text("Header")
+///     .nested(inner_description);
+/// verify_that!(outer_description, displays_as(eq("\
+/// Header
+///   A block
+///   Another block")))
+/// # .unwrap();
+/// ```
+///
+/// One can also enumerate or bullet list the elements of a `Description`:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// # use googletest::description::Description;
+/// let description = Description::new()
+///     .text("First item")
+///     .text("Second item")
+///     .bullet_list();
+/// verify_that!(description, displays_as(eq("\
+/// * First item
+/// * Second item")))
+/// # .unwrap();
+/// ```
+///
+/// One can construct a `Description` from a [`String`] or a string slice, an
+/// iterator thereof, or from an iterator over other `Description`s:
+///
+/// ```
+/// # use googletest::description::Description;
+/// let single_element_description: Description =
+///     "A single block description".into();
+/// let two_element_description: Description =
+///     ["First item", "Second item"].into_iter().collect();
+/// let two_element_description_from_strings: Description =
+///     ["First item".to_string(), "Second item".to_string()].into_iter().collect();
+/// ```
+///
+/// No newline is added after the last element during rendering. This makes it
+/// easier to support single-line matcher descriptions and match explanations.
+#[derive(Debug, Default)]
+pub struct Description {
+    elements: List,
+    initial_indentation: usize,
+}
+
+impl Description {
+    /// Returns a new empty [`Description`].
+    pub fn new() -> Self {
+        Default::default()
+    }
+
+    /// Appends a block of text to this instance.
+    ///
+    /// The block is indented uniformly when this instance is rendered.
+    pub fn text(mut self, text: impl Into<Cow<'static, str>>) -> Self {
+        self.elements.push_literal(text.into());
+        self
+    }
+
+    /// Appends a nested [`Description`] to this instance.
+    ///
+    /// The nested [`Description`] `inner` is indented uniformly at the next
+    /// level of indentation when this instance is rendered.
+    pub fn nested(mut self, inner: Description) -> Self {
+        self.elements.push_nested(inner.elements);
+        self
+    }
+
+    /// Appends all [`Description`] in the given sequence `inner` to this
+    /// instance.
+    ///
+    /// Each element is treated as a nested [`Description`] in the sense of
+    /// [`Self::nested`].
+    pub fn collect(self, inner: impl IntoIterator<Item = Description>) -> Self {
+        inner.into_iter().fold(self, |outer, inner| outer.nested(inner))
+    }
+
+    /// Indents the lines in elements of this description.
+    ///
+    /// This operation will be performed lazily when [`self`] is displayed.
+    ///
+    /// This will indent every line inside each element.
+    ///
+    /// For example:
+    ///
+    /// ```
+    /// # use googletest::prelude::*;
+    /// # use googletest::description::Description;
+    /// let description = std::iter::once("A B C\nD E F".to_string()).collect::<Description>();
+    /// verify_that!(description.indent(), displays_as(eq("  A B C\n  D E F")))
+    /// # .unwrap();
+    /// ```
+    pub fn indent(self) -> Self {
+        Self { initial_indentation: INDENTATION_SIZE, ..self }
+    }
+
+    /// Instructs this instance to render its elements as a bullet list.
+    ///
+    /// Each element (from either [`Description::text`] or
+    /// [`Description::nested`]) is rendered as a bullet point. If an element
+    /// contains multiple lines, the following lines are aligned with the first
+    /// one in the block.
+    ///
+    /// For instance:
+    ///
+    /// ```
+    /// # use googletest::prelude::*;
+    /// # use googletest::description::Description;
+    /// let description = Description::new()
+    ///     .text("First line\nsecond line")
+    ///     .bullet_list();
+    /// verify_that!(description, displays_as(eq("\
+    /// * First line
+    ///   second line")))
+    /// # .unwrap();
+    /// ```
+    pub fn bullet_list(self) -> Self {
+        Self { elements: self.elements.bullet_list(), ..self }
+    }
+
+    /// Instructs this instance to render its elements as an enumerated list.
+    ///
+    /// Each element (from either [`Description::text`] or
+    /// [`Description::nested`]) is rendered with its zero-based index. If an
+    /// element contains multiple lines, the following lines are aligned with
+    /// the first one in the block.
+    ///
+    /// For instance:
+    ///
+    /// ```
+    /// # use googletest::prelude::*;
+    /// # use googletest::description::Description;
+    /// let description = Description::new()
+    ///     .text("First line\nsecond line")
+    ///     .enumerate();
+    /// verify_that!(description, displays_as(eq("\
+    /// 0. First line
+    ///    second line")))
+    /// # .unwrap();
+    /// ```
+    pub fn enumerate(self) -> Self {
+        Self { elements: self.elements.enumerate(), ..self }
+    }
+
+    /// Returns the length of elements.
+    pub fn len(&self) -> usize {
+        self.elements.len()
+    }
+
+    /// Returns whether the set of elements is empty.
+    pub fn is_empty(&self) -> bool {
+        self.elements.is_empty()
+    }
+}
+
+impl Display for Description {
+    fn fmt(&self, f: &mut Formatter) -> Result {
+        self.elements.render(f, self.initial_indentation)
+    }
+}
+
+impl<ElementT: Into<Cow<'static, str>>> FromIterator<ElementT> for Description {
+    fn from_iter<T>(iter: T) -> Self
+    where
+        T: IntoIterator<Item = ElementT>,
+    {
+        Self { elements: iter.into_iter().map(ElementT::into).collect(), ..Default::default() }
+    }
+}
+
+impl FromIterator<Description> for Description {
+    fn from_iter<T>(iter: T) -> Self
+    where
+        T: IntoIterator<Item = Description>,
+    {
+        Self { elements: iter.into_iter().map(|s| s.elements).collect(), ..Default::default() }
+    }
+}
+
+impl<T: Into<Cow<'static, str>>> From<T> for Description {
+    fn from(value: T) -> Self {
+        let mut elements = List::default();
+        elements.push_literal(value.into());
+        Self { elements, ..Default::default() }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::Description;
+    use crate::prelude::*;
+    use indoc::indoc;
+
+    #[test]
+    fn renders_single_fragment() -> Result<()> {
+        let description: Description = "A B C".into();
+        verify_that!(description, displays_as(eq("A B C")))
+    }
+
+    #[test]
+    fn renders_two_fragments() -> Result<()> {
+        let description =
+            ["A B C".to_string(), "D E F".to_string()].into_iter().collect::<Description>();
+        verify_that!(description, displays_as(eq("A B C\nD E F")))
+    }
+
+    #[test]
+    fn nested_description_is_indented() -> Result<()> {
+        let description = Description::new()
+            .text("Header")
+            .nested(["A B C".to_string()].into_iter().collect::<Description>());
+        verify_that!(description, displays_as(eq("Header\n  A B C")))
+    }
+
+    #[test]
+    fn nested_description_indents_two_elements() -> Result<()> {
+        let description = Description::new().text("Header").nested(
+            ["A B C".to_string(), "D E F".to_string()].into_iter().collect::<Description>(),
+        );
+        verify_that!(description, displays_as(eq("Header\n  A B C\n  D E F")))
+    }
+
+    #[test]
+    fn nested_description_indents_one_element_on_two_lines() -> Result<()> {
+        let description = Description::new().text("Header").nested("A B C\nD E F".into());
+        verify_that!(description, displays_as(eq("Header\n  A B C\n  D E F")))
+    }
+
+    #[test]
+    fn single_fragment_renders_with_bullet_when_bullet_list_enabled() -> Result<()> {
+        let description = Description::new().text("A B C").bullet_list();
+        verify_that!(description, displays_as(eq("* A B C")))
+    }
+
+    #[test]
+    fn single_nested_fragment_renders_with_bullet_when_bullet_list_enabled() -> Result<()> {
+        let description = Description::new().nested("A B C".into()).bullet_list();
+        verify_that!(description, displays_as(eq("* A B C")))
+    }
+
+    #[test]
+    fn two_fragments_render_with_bullet_when_bullet_list_enabled() -> Result<()> {
+        let description = Description::new().text("A B C").text("D E F").bullet_list();
+        verify_that!(description, displays_as(eq("* A B C\n* D E F")))
+    }
+
+    #[test]
+    fn two_nested_fragments_render_with_bullet_when_bullet_list_enabled() -> Result<()> {
+        let description =
+            Description::new().nested("A B C".into()).nested("D E F".into()).bullet_list();
+        verify_that!(description, displays_as(eq("* A B C\n* D E F")))
+    }
+
+    #[test]
+    fn single_fragment_with_more_than_one_line_renders_with_one_bullet() -> Result<()> {
+        let description = Description::new().text("A B C\nD E F").bullet_list();
+        verify_that!(description, displays_as(eq("* A B C\n  D E F")))
+    }
+
+    #[test]
+    fn single_fragment_renders_with_enumeration_when_enumerate_enabled() -> Result<()> {
+        let description = Description::new().text("A B C").enumerate();
+        verify_that!(description, displays_as(eq("0. A B C")))
+    }
+
+    #[test]
+    fn two_fragments_render_with_enumeration_when_enumerate_enabled() -> Result<()> {
+        let description = Description::new().text("A B C").text("D E F").enumerate();
+        verify_that!(description, displays_as(eq("0. A B C\n1. D E F")))
+    }
+
+    #[test]
+    fn single_fragment_with_two_lines_renders_with_one_enumeration_label() -> Result<()> {
+        let description = Description::new().text("A B C\nD E F").enumerate();
+        verify_that!(description, displays_as(eq("0. A B C\n   D E F")))
+    }
+
+    #[test]
+    fn multi_digit_enumeration_renders_with_correct_offset() -> Result<()> {
+        let description = ["A B C\nD E F"; 11]
+            .into_iter()
+            .map(str::to_string)
+            .collect::<Description>()
+            .enumerate();
+        verify_that!(
+            description,
+            displays_as(eq(indoc!(
+                "
+                 0. A B C
+                    D E F
+                 1. A B C
+                    D E F
+                 2. A B C
+                    D E F
+                 3. A B C
+                    D E F
+                 4. A B C
+                    D E F
+                 5. A B C
+                    D E F
+                 6. A B C
+                    D E F
+                 7. A B C
+                    D E F
+                 8. A B C
+                    D E F
+                 9. A B C
+                    D E F
+                10. A B C
+                    D E F"
+            )))
+        )
+    }
+}
diff --git a/src/internal/description_renderer.rs b/src/internal/description_renderer.rs
new file mode 100644
index 0000000..937f0d5
--- /dev/null
+++ b/src/internal/description_renderer.rs
@@ -0,0 +1,614 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::{
+    borrow::Cow,
+    fmt::{Result, Write},
+};
+
+/// Number of space used to indent lines when no alignement is required.
+pub(crate) const INDENTATION_SIZE: usize = 2;
+
+/// A list of [`Block`] possibly rendered with a [`Decoration`].
+///
+/// This is the top-level renderable component, corresponding to the description
+/// or match explanation of a single matcher.
+///
+/// The constituent [`Block`] of a `List` can be decorated with either bullets
+/// (`* `) or enumeration (`0. `, `1. `, ...). This is controlled via the
+/// methods [`List::bullet_list`] and [`List::enumerate`]. By default, there is
+/// no decoration.
+///
+/// A `List` can be constructed as follows:
+///
+///   * [`Default::default()`] constructs an empty `List`.
+///   * [`Iterator::collect()`] on an [`Iterator`] of [`Block`].
+///   * [`Iterator::collect()`] on an [`Iterator`] of `String`, which produces a
+///     [`Block::Literal`] for each `String`.
+///   * [`Iterator::collect()`] on an [`Iterator`] of `List`, which produces a
+///     [`Block::Nested`] for each `List`.
+#[derive(Debug, Default)]
+pub(crate) struct List(Vec<Block>, Decoration);
+
+impl List {
+    /// Render this instance using the formatter `f`.
+    ///
+    /// Indent each line of output by `indentation` spaces.
+    pub(crate) fn render(&self, f: &mut dyn Write, indentation: usize) -> Result {
+        self.render_with_prefix(f, indentation, "".into())
+    }
+
+    /// Append a new [`Block`] containing `literal`.
+    ///
+    /// The input `literal` is split into lines so that each line will be
+    /// indented correctly.
+    pub(crate) fn push_literal(&mut self, literal: Cow<'static, str>) {
+        self.0.push(literal.into());
+    }
+
+    /// Append a new [`Block`] containing `inner` as a nested [`List`].
+    pub(crate) fn push_nested(&mut self, inner: List) {
+        self.0.push(Block::Nested(inner));
+    }
+
+    /// Render each [`Block`] of this instance preceded with a bullet "* ".
+    pub(crate) fn bullet_list(self) -> Self {
+        Self(self.0, Decoration::Bullet)
+    }
+
+    /// Render each [`Block`] of this instance preceded with its 0-based index.
+    pub(crate) fn enumerate(self) -> Self {
+        Self(self.0, Decoration::Enumerate)
+    }
+
+    /// Return the number of [`Block`] in this instance.
+    pub(crate) fn len(&self) -> usize {
+        self.0.len()
+    }
+
+    /// Return `true` if there are no [`Block`] in this instance, `false`
+    /// otherwise.
+    pub(crate) fn is_empty(&self) -> bool {
+        self.0.is_empty()
+    }
+
+    fn render_with_prefix(
+        &self,
+        f: &mut dyn Write,
+        indentation: usize,
+        prefix: Cow<'static, str>,
+    ) -> Result {
+        if self.0.is_empty() {
+            return Ok(());
+        }
+
+        let enumeration_padding = self.enumeration_padding();
+
+        self.0[0].render(
+            f,
+            indentation,
+            self.full_prefix(0, enumeration_padding, &prefix).into(),
+        )?;
+        for (index, block) in self.0[1..].iter().enumerate() {
+            writeln!(f)?;
+            block.render(
+                f,
+                indentation + prefix.len(),
+                self.prefix(index + 1, enumeration_padding),
+            )?;
+        }
+        Ok(())
+    }
+
+    fn full_prefix(&self, index: usize, enumeration_padding: usize, prior_prefix: &str) -> String {
+        format!("{prior_prefix}{}", self.prefix(index, enumeration_padding))
+    }
+
+    fn prefix(&self, index: usize, enumeration_padding: usize) -> Cow<'static, str> {
+        match self.1 {
+            Decoration::None => "".into(),
+            Decoration::Bullet => "* ".into(),
+            Decoration::Enumerate => format!("{:>enumeration_padding$}. ", index).into(),
+        }
+    }
+
+    fn enumeration_padding(&self) -> usize {
+        match self.1 {
+            Decoration::None => 0,
+            Decoration::Bullet => 0,
+            Decoration::Enumerate => {
+                if self.0.len() > 1 {
+                    ((self.0.len() - 1) as f64).log10().floor() as usize + 1
+                } else {
+                    // Avoid negative logarithm when there is only 0 or 1 element.
+                    1
+                }
+            }
+        }
+    }
+}
+
+impl FromIterator<Block> for List {
+    fn from_iter<T>(iter: T) -> Self
+    where
+        T: IntoIterator<Item = Block>,
+    {
+        Self(iter.into_iter().collect(), Decoration::None)
+    }
+}
+
+impl<ElementT: Into<Cow<'static, str>>> FromIterator<ElementT> for List {
+    fn from_iter<T>(iter: T) -> Self
+    where
+        T: IntoIterator<Item = ElementT>,
+    {
+        Self(iter.into_iter().map(|b| b.into().into()).collect(), Decoration::None)
+    }
+}
+
+impl FromIterator<List> for List {
+    fn from_iter<T>(iter: T) -> Self
+    where
+        T: IntoIterator<Item = List>,
+    {
+        Self(iter.into_iter().map(Block::nested).collect(), Decoration::None)
+    }
+}
+
+/// A sequence of [`Fragment`] or a nested [`List`].
+///
+/// This may be rendered with a prefix specified by the [`Decoration`] of the
+/// containing [`List`]. In this case, all lines are indented to align with the
+/// first character of the first line of the block.
+#[derive(Debug)]
+enum Block {
+    /// A block of text.
+    ///
+    /// Each constituent [`Fragment`] contains one line of text. The lines are
+    /// indented uniformly to the current indentation of this block when
+    /// rendered.
+    Literal(Vec<Fragment>),
+
+    /// A nested [`List`].
+    ///
+    /// The [`List`] is rendered recursively at the next level of indentation.
+    Nested(List),
+}
+
+impl Block {
+    fn nested(inner: List) -> Self {
+        Self::Nested(inner)
+    }
+
+    fn render(&self, f: &mut dyn Write, indentation: usize, prefix: Cow<'static, str>) -> Result {
+        match self {
+            Self::Literal(fragments) => {
+                if fragments.is_empty() {
+                    return Ok(());
+                }
+
+                write!(f, "{:indentation$}{prefix}", "")?;
+                fragments[0].render(f)?;
+                let block_indentation = indentation + prefix.as_ref().len();
+                for fragment in &fragments[1..] {
+                    writeln!(f)?;
+                    write!(f, "{:block_indentation$}", "")?;
+                    fragment.render(f)?;
+                }
+                Ok(())
+            }
+            Self::Nested(inner) => inner.render_with_prefix(
+                f,
+                indentation + INDENTATION_SIZE.saturating_sub(prefix.len()),
+                prefix,
+            ),
+        }
+    }
+}
+
+impl From<String> for Block {
+    fn from(value: String) -> Self {
+        Block::Literal(value.lines().map(|v| Fragment(v.to_string().into())).collect())
+    }
+}
+
+impl From<&'static str> for Block {
+    fn from(value: &'static str) -> Self {
+        Block::Literal(value.lines().map(|v| Fragment(v.into())).collect())
+    }
+}
+
+impl From<Cow<'static, str>> for Block {
+    fn from(value: Cow<'static, str>) -> Self {
+        match value {
+            Cow::Borrowed(value) => value.into(),
+            Cow::Owned(value) => value.into(),
+        }
+    }
+}
+
+/// A string representing one line of a description or match explanation.
+#[derive(Debug)]
+struct Fragment(Cow<'static, str>);
+
+impl Fragment {
+    fn render(&self, f: &mut dyn Write) -> Result {
+        write!(f, "{}", self.0)
+    }
+}
+
+/// The decoration which appears on [`Block`] of a [`List`] when rendered.
+#[derive(Debug, Default)]
+enum Decoration {
+    /// No decoration on each [`Block`]. The default.
+    #[default]
+    None,
+
+    /// Each [`Block`] is preceded by a bullet (`* `).
+    Bullet,
+
+    /// Each [`Block`] is preceded by its index in the [`List`] (`0. `, `1. `,
+    /// ...).
+    Enumerate,
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{Block, Fragment, List};
+    use crate::prelude::*;
+    use indoc::indoc;
+
+    #[test]
+    fn renders_fragment() -> Result<()> {
+        let fragment = Fragment("A fragment".into());
+        let mut result = String::new();
+
+        fragment.render(&mut result)?;
+
+        verify_that!(result, eq("A fragment"))
+    }
+
+    #[test]
+    fn renders_empty_block() -> Result<()> {
+        let block = Block::Literal(vec![]);
+        let mut result = String::new();
+
+        block.render(&mut result, 0, "".into())?;
+
+        verify_that!(result, eq(""))
+    }
+
+    #[test]
+    fn renders_block_with_one_fragment() -> Result<()> {
+        let block: Block = "A fragment".into();
+        let mut result = String::new();
+
+        block.render(&mut result, 0, "".into())?;
+
+        verify_that!(result, eq("A fragment"))
+    }
+
+    #[test]
+    fn renders_block_with_two_fragments() -> Result<()> {
+        let block: Block = "A fragment\nAnother fragment".into();
+        let mut result = String::new();
+
+        block.render(&mut result, 0, "".into())?;
+
+        verify_that!(result, eq("A fragment\nAnother fragment"))
+    }
+
+    #[test]
+    fn renders_indented_block() -> Result<()> {
+        let block: Block = "A fragment\nAnother fragment".into();
+        let mut result = String::new();
+
+        block.render(&mut result, 2, "".into())?;
+
+        verify_that!(result, eq("  A fragment\n  Another fragment"))
+    }
+
+    #[test]
+    fn renders_block_with_prefix() -> Result<()> {
+        let block: Block = "A fragment\nAnother fragment".into();
+        let mut result = String::new();
+
+        block.render(&mut result, 0, "* ".into())?;
+
+        verify_that!(result, eq("* A fragment\n  Another fragment"))
+    }
+
+    #[test]
+    fn renders_indented_block_with_prefix() -> Result<()> {
+        let block: Block = "A fragment\nAnother fragment".into();
+        let mut result = String::new();
+
+        block.render(&mut result, 2, "* ".into())?;
+
+        verify_that!(result, eq("  * A fragment\n    Another fragment"))
+    }
+
+    #[test]
+    fn renders_empty_list() -> Result<()> {
+        let list = list(vec![]);
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq(""))
+    }
+
+    #[test]
+    fn renders_plain_list_with_one_block() -> Result<()> {
+        let list = list(vec!["A fragment".into()]);
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("A fragment"))
+    }
+
+    #[test]
+    fn renders_plain_list_with_two_blocks() -> Result<()> {
+        let list = list(vec!["A fragment".into(), "A fragment in a second block".into()]);
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("A fragment\nA fragment in a second block"))
+    }
+
+    #[test]
+    fn renders_plain_list_with_one_block_with_two_fragments() -> Result<()> {
+        let list = list(vec!["A fragment\nA second fragment".into()]);
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("A fragment\nA second fragment"))
+    }
+
+    #[test]
+    fn renders_nested_plain_list_with_one_block() -> Result<()> {
+        let list = list(vec![Block::nested(list(vec!["A fragment".into()]))]);
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("  A fragment"))
+    }
+
+    #[test]
+    fn renders_nested_plain_list_with_two_blocks() -> Result<()> {
+        let list = list(vec![Block::nested(list(vec![
+            "A fragment".into(),
+            "A fragment in a second block".into(),
+        ]))]);
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("  A fragment\n  A fragment in a second block"))
+    }
+
+    #[test]
+    fn renders_nested_plain_list_with_one_block_with_two_fragments() -> Result<()> {
+        let list = list(vec![Block::nested(list(vec!["A fragment\nA second fragment".into()]))]);
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("  A fragment\n  A second fragment"))
+    }
+
+    #[test]
+    fn renders_bulleted_list_with_one_block() -> Result<()> {
+        let list = list(vec!["A fragment".into()]).bullet_list();
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("* A fragment"))
+    }
+
+    #[test]
+    fn renders_bulleted_list_with_two_blocks() -> Result<()> {
+        let list =
+            list(vec!["A fragment".into(), "A fragment in a second block".into()]).bullet_list();
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("* A fragment\n* A fragment in a second block"))
+    }
+
+    #[test]
+    fn renders_bulleted_list_with_one_block_with_two_fragments() -> Result<()> {
+        let list = list(vec!["A fragment\nA second fragment".into()]).bullet_list();
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("* A fragment\n  A second fragment"))
+    }
+
+    #[test]
+    fn renders_nested_bulleted_list_with_one_block() -> Result<()> {
+        let list = list(vec![Block::nested(list(vec!["A fragment".into()]).bullet_list())]);
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("  * A fragment"))
+    }
+
+    #[test]
+    fn renders_nested_bulleted_list_with_two_blocks() -> Result<()> {
+        let list = list(vec![Block::nested(
+            list(vec!["A fragment".into(), "A fragment in a second block".into()]).bullet_list(),
+        )]);
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("  * A fragment\n  * A fragment in a second block"))
+    }
+
+    #[test]
+    fn renders_nested_bulleted_list_with_one_block_with_two_fragments() -> Result<()> {
+        let list = list(vec![Block::nested(
+            list(vec!["A fragment\nA second fragment".into()]).bullet_list(),
+        )]);
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("  * A fragment\n    A second fragment"))
+    }
+
+    #[test]
+    fn renders_enumerated_list_with_one_block() -> Result<()> {
+        let list = list(vec!["A fragment".into()]).enumerate();
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("0. A fragment"))
+    }
+
+    #[test]
+    fn renders_enumerated_list_with_two_blocks() -> Result<()> {
+        let list =
+            list(vec!["A fragment".into(), "A fragment in a second block".into()]).enumerate();
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("0. A fragment\n1. A fragment in a second block"))
+    }
+
+    #[test]
+    fn renders_enumerated_list_with_one_block_with_two_fragments() -> Result<()> {
+        let list = list(vec!["A fragment\nA second fragment".into()]).enumerate();
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("0. A fragment\n   A second fragment"))
+    }
+
+    #[test]
+    fn aligns_renders_enumerated_list_with_more_than_ten_blocks() -> Result<()> {
+        let list =
+            (0..11).map(|i| Block::from(format!("Fragment {i}"))).collect::<List>().enumerate();
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(
+            result,
+            eq(indoc! {"
+                 0. Fragment 0
+                 1. Fragment 1
+                 2. Fragment 2
+                 3. Fragment 3
+                 4. Fragment 4
+                 5. Fragment 5
+                 6. Fragment 6
+                 7. Fragment 7
+                 8. Fragment 8
+                 9. Fragment 9
+                10. Fragment 10"})
+        )
+    }
+
+    #[test]
+    fn renders_fragment_plus_nested_plain_list_with_one_block() -> Result<()> {
+        let list =
+            list(vec!["A fragment".into(), Block::nested(list(vec!["Another fragment".into()]))]);
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("A fragment\n  Another fragment"))
+    }
+
+    #[test]
+    fn renders_double_nested_plain_list_with_one_block() -> Result<()> {
+        let list =
+            list(vec![Block::nested(list(vec![Block::nested(list(vec!["A fragment".into()]))]))]);
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("    A fragment"))
+    }
+
+    #[test]
+    fn renders_headers_plus_double_nested_plain_list() -> Result<()> {
+        let list = list(vec![
+            "First header".into(),
+            Block::nested(list(vec![
+                "Second header".into(),
+                Block::nested(list(vec!["A fragment".into()])),
+            ])),
+        ]);
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("First header\n  Second header\n    A fragment"))
+    }
+
+    #[test]
+    fn renders_double_nested_bulleted_list() -> Result<()> {
+        let list =
+            list(vec![Block::nested(list(vec!["A fragment".into()]).bullet_list())]).bullet_list();
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("* * A fragment"))
+    }
+
+    #[test]
+    fn renders_nested_enumeration_with_two_blocks_inside_bulleted_list() -> Result<()> {
+        let list =
+            list(vec![Block::nested(list(vec!["Block 1".into(), "Block 2".into()]).enumerate())])
+                .bullet_list();
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("* 0. Block 1\n  1. Block 2"))
+    }
+
+    #[test]
+    fn renders_nested_enumeration_with_block_with_two_fragments_inside_bulleted_list() -> Result<()>
+    {
+        let list = list(vec![Block::nested(
+            list(vec!["A fragment\nAnother fragment".into()]).enumerate(),
+        )])
+        .bullet_list();
+        let mut result = String::new();
+
+        list.render(&mut result, 0)?;
+
+        verify_that!(result, eq("* 0. A fragment\n     Another fragment"))
+    }
+
+    fn list(blocks: Vec<Block>) -> List {
+        List(blocks, super::Decoration::None)
+    }
+}
diff --git a/src/internal/mod.rs b/src/internal/mod.rs
index 2f37418..9bccdc3 100644
--- a/src/internal/mod.rs
+++ b/src/internal/mod.rs
@@ -14,5 +14,6 @@
 
 #![doc(hidden)]
 
+pub(crate) mod description_renderer;
 pub mod source_location;
 pub mod test_outcome;
diff --git a/src/internal/test_outcome.rs b/src/internal/test_outcome.rs
index 2171cc7..132ba99 100644
--- a/src/internal/test_outcome.rs
+++ b/src/internal/test_outcome.rs
@@ -174,7 +174,7 @@
 
     pub(crate) fn log(&self) {
         TestOutcome::fail_current_test();
-        print!("{}", self);
+        println!("{}", self);
     }
 }
 
diff --git a/src/lib.rs b/src/lib.rs
index cec6da0..51b6345 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -21,6 +21,7 @@
 
 #[macro_use]
 pub mod assertions;
+pub mod description;
 pub mod internal;
 pub mod matcher;
 pub mod matcher_support;
@@ -49,11 +50,6 @@
     pub use super::Result;
     // Assert macros
     pub use super::{assert_that, expect_pred, expect_that, fail, verify_pred, verify_that};
-    // Matcher macros
-    pub use super::{
-        all, any, contains_each, elements_are, field, is_contained_in, matches_pattern, pat,
-        pointwise, property, unordered_elements_are,
-    };
 }
 
 pub use googletest_macro::test;
@@ -62,7 +58,10 @@
 
 /// A `Result` whose `Err` variant indicates a test failure.
 ///
-/// All test functions should return `Result<()>`.
+/// The assertions [`verify_that!`][crate::verify_that],
+/// [`verify_pred!`][crate::verify_pred], and [`fail!`][crate::fail] evaluate
+/// to `Result<()>`. A test function may return `Result<()>` in combination with
+/// those macros to abort immediately on assertion failure.
 ///
 /// This can be used with subroutines which may cause the test to fatally fail
 /// and which return some value needed by the caller. For example:
diff --git a/src/matcher.rs b/src/matcher.rs
index 83a4475..071b0d1 100644
--- a/src/matcher.rs
+++ b/src/matcher.rs
@@ -14,10 +14,11 @@
 
 //! The components required to implement matchers.
 
+use crate::description::Description;
 use crate::internal::source_location::SourceLocation;
 use crate::internal::test_outcome::TestAssertionFailure;
-use crate::matchers::conjunction_matcher::ConjunctionMatcher;
-use crate::matchers::disjunction_matcher::DisjunctionMatcher;
+use crate::matchers::__internal_unstable_do_not_depend_on_these::ConjunctionMatcher;
+use crate::matchers::__internal_unstable_do_not_depend_on_these::DisjunctionMatcher;
 use std::fmt::Debug;
 
 /// An interface for checking an arbitrary condition on a datum.
@@ -57,11 +58,13 @@
     /// [`some`][crate::matchers::some] implements `describe` as follows:
     ///
     /// ```ignore
-    /// fn describe(&self, matcher_result: MatcherResult) -> String {
+    /// fn describe(&self, matcher_result: MatcherResult) -> Description {
     ///     match matcher_result {
     ///         MatcherResult::Matches => {
-    ///             format!("has a value which {}", self.inner.describe(MatcherResult::Matches))
-    ///               //  Inner matcher invocation: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+    ///             Description::new()
+    ///                 .text("has a value which")
+    ///                 .nested(self.inner.describe(MatcherResult::Matches))
+    ///       // Inner matcher: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     ///         }
     ///         MatcherResult::DoesNotMatch => {...} // Similar to the above
     ///     }
@@ -75,7 +78,7 @@
     /// output of `explain_match` is always used adjectivally to describe the
     /// actual value, while `describe` is used in contexts where a relative
     /// clause would not make sense.
-    fn describe(&self, matcher_result: MatcherResult) -> String;
+    fn describe(&self, matcher_result: MatcherResult) -> Description;
 
     /// Prepares a [`String`] describing how the expected value
     /// encoded in this instance matches or does not match the given value
@@ -114,7 +117,7 @@
     /// inner matcher and appears as follows:
     ///
     /// ```ignore
-    /// fn explain_match(&self, actual: &Self::ActualT) -> String {
+    /// fn explain_match(&self, actual: &Self::ActualT) -> Description {
     ///     self.expected.explain_match(actual.deref())
     /// }
     /// ```
@@ -124,12 +127,14 @@
     /// inner matcher at a point where a relative clause would fit. For example:
     ///
     /// ```ignore
-    /// fn explain_match(&self, actual: &Self::ActualT) -> String {
-    ///     format!("which points to a value {}", self.expected.explain_match(actual.deref()))
+    /// fn explain_match(&self, actual: &Self::ActualT) -> Description {
+    ///     Description::new()
+    ///         .text("which points to a value")
+    ///         .nested(self.expected.explain_match(actual.deref()))
     /// }
     /// ```
-    fn explain_match(&self, actual: &Self::ActualT) -> String {
-        format!("which {}", self.describe(self.matches(actual)))
+    fn explain_match(&self, actual: &Self::ActualT) -> Description {
+        format!("which {}", self.describe(self.matches(actual))).into()
     }
 
     /// Constructs a matcher that matches both `self` and `right`.
@@ -222,10 +227,10 @@
 Value of: {actual_expr}
 Expected: {}
 Actual: {actual_formatted},
-  {}
+{}
 {source_location}",
         matcher.describe(MatcherResult::Match),
-        matcher.explain_match(actual),
+        matcher.explain_match(actual).indent(),
     ))
 }
 
diff --git a/src/matcher_support/description.rs b/src/matcher_support/description.rs
deleted file mode 100644
index ce074e0..0000000
--- a/src/matcher_support/description.rs
+++ /dev/null
@@ -1,410 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::fmt::{Display, Formatter, Result};
-
-/// Helper structure to build better output of
-/// [`Matcher::describe`][crate::matcher::Matcher::describe] and
-/// [`Matcher::explain_match`][crate::matcher::Matcher::explain_match]. This
-/// is especially useful with composed matchers and matchers over containers.
-///
-/// It provides simple operations to lazily format lists of strings.
-///
-/// Usage:
-/// ```ignore
-/// let iter: impl Iterator<String> = ...
-/// format!("{}", iter.collect::<Description>().indent().bullet_list())
-/// ```
-///
-/// To construct a [`Description`], use `Iterator<Item=String>::collect()`.
-/// Each element of the collected iterator will be separated by a
-/// newline when displayed. The elements may be multi-line, but they will
-/// nevertheless be indented consistently.
-///
-/// Note that a newline will only be added between each element, but not
-/// after the last element. This makes it simpler to keep
-/// [`Matcher::describe`][crate::matcher::Matcher::describe]
-/// and [`Matcher::explain_match`][crate::matcher::Matcher::explain_match]
-/// consistent with simpler [`Matchers`][crate::matcher::Matcher].
-///
-/// They can also be indented, enumerated and or
-/// bullet listed if [`Description::indent`], [`Description::enumerate`], or
-/// respectively [`Description::bullet_list`] has been called.
-#[derive(Debug)]
-pub struct Description {
-    elements: Vec<String>,
-    indent_mode: IndentMode,
-    list_style: ListStyle,
-}
-
-#[derive(Debug)]
-enum IndentMode {
-    NoIndent,
-    EveryLine,
-    AllExceptFirstLine,
-}
-
-#[derive(Debug)]
-enum ListStyle {
-    NoList,
-    Bullet,
-    Enumerate,
-}
-
-struct IndentationSizes {
-    first_line_indent: usize,
-    first_line_of_element_indent: usize,
-    enumeration_padding: usize,
-    other_line_indent: usize,
-}
-
-/// Number of space used to indent lines when no alignement is required.
-const INDENTATION_SIZE: usize = 2;
-
-impl Description {
-    /// Indents the lines in elements of this description.
-    ///
-    /// This operation will be performed lazily when [`self`] is displayed.
-    ///
-    /// This will indent every line inside each element.
-    ///
-    /// For example:
-    ///
-    /// ```
-    /// # use googletest::prelude::*;
-    /// # use googletest::matcher_support::description::Description;
-    /// let description = std::iter::once("A B C\nD E F".to_string()).collect::<Description>();
-    /// verify_that!(description.indent(), displays_as(eq("  A B C\n  D E F")))
-    /// # .unwrap();
-    /// ```
-    pub fn indent(self) -> Self {
-        Self { indent_mode: IndentMode::EveryLine, ..self }
-    }
-
-    /// Indents the lines in elements of this description except for the first
-    /// line.
-    ///
-    /// This is similar to [`Self::indent`] except that the first line is not
-    /// indented. This is useful when the first line has already been indented
-    /// in the output.
-    ///
-    /// For example:
-    ///
-    /// ```
-    /// # use googletest::prelude::*;
-    /// # use googletest::matcher_support::description::Description;
-    /// let description = std::iter::once("A B C\nD E F".to_string()).collect::<Description>();
-    /// verify_that!(description.indent_except_first_line(), displays_as(eq("A B C\n  D E F")))
-    /// # .unwrap();
-    /// ```
-    pub fn indent_except_first_line(self) -> Self {
-        Self { indent_mode: IndentMode::AllExceptFirstLine, ..self }
-    }
-
-    /// Bullet lists the elements of [`self`].
-    ///
-    /// This operation will be performed lazily when [`self`] is displayed.
-    ///
-    /// Note that this will only bullet list each element, not each line
-    /// in each element.
-    ///
-    /// For instance:
-    ///
-    /// ```
-    /// # use googletest::prelude::*;
-    /// # use googletest::matcher_support::description::Description;
-    /// let description = std::iter::once("A B C\nD E F".to_string()).collect::<Description>();
-    /// verify_that!(description.bullet_list(), displays_as(eq("* A B C\n  D E F")))
-    /// # .unwrap();
-    /// ```
-    pub fn bullet_list(self) -> Self {
-        Self { list_style: ListStyle::Bullet, ..self }
-    }
-
-    /// Enumerates the elements of [`self`].
-    ///
-    /// This operation will be performed lazily when [`self`] is displayed.
-    ///
-    /// Note that this will only enumerate each element, not each line in
-    /// each element.
-    ///
-    /// For instance:
-    ///
-    /// ```
-    /// # use googletest::prelude::*;
-    /// # use googletest::matcher_support::description::Description;
-    /// let description = std::iter::once("A B C\nD E F".to_string()).collect::<Description>();
-    /// verify_that!(description.enumerate(), displays_as(eq("0. A B C\n   D E F")))
-    /// # .unwrap();
-    /// ```
-    pub fn enumerate(self) -> Self {
-        Self { list_style: ListStyle::Enumerate, ..self }
-    }
-
-    /// Returns the length of elements.
-    pub fn len(&self) -> usize {
-        self.elements.len()
-    }
-
-    /// Returns whether the set of elements is empty.
-    pub fn is_empty(&self) -> bool {
-        self.elements.is_empty()
-    }
-
-    fn indentation_sizes(&self) -> IndentationSizes {
-        let first_line_indent =
-            if matches!(self.indent_mode, IndentMode::EveryLine) { INDENTATION_SIZE } else { 0 };
-        let first_line_of_element_indent =
-            if !matches!(self.indent_mode, IndentMode::NoIndent) { INDENTATION_SIZE } else { 0 };
-        // Number of digit of the last index. For instance, an array of length 13 will
-        // have 12 as last index (we start at 0), which have a digit size of 2.
-        let enumeration_padding = if self.elements.len() > 1 {
-            ((self.elements.len() - 1) as f64).log10().floor() as usize + 1
-        } else {
-            // Avoid negative logarithm when there is only 0 or 1 element.
-            1
-        };
-
-        let other_line_indent = first_line_of_element_indent
-            + match self.list_style {
-                ListStyle::NoList => 0,
-                ListStyle::Bullet => "* ".len(),
-                ListStyle::Enumerate => enumeration_padding + ". ".len(),
-            };
-        IndentationSizes {
-            first_line_indent,
-            first_line_of_element_indent,
-            enumeration_padding,
-            other_line_indent,
-        }
-    }
-}
-
-impl Display for Description {
-    fn fmt(&self, f: &mut Formatter) -> Result {
-        let IndentationSizes {
-            mut first_line_indent,
-            first_line_of_element_indent,
-            enumeration_padding,
-            other_line_indent,
-        } = self.indentation_sizes();
-
-        let mut first = true;
-        for (idx, element) in self.elements.iter().enumerate() {
-            let mut lines = element.lines();
-            if let Some(line) = lines.next() {
-                if first {
-                    first = false;
-                } else {
-                    writeln!(f)?;
-                }
-                match self.list_style {
-                    ListStyle::NoList => {
-                        write!(f, "{:first_line_indent$}{line}", "")?;
-                    }
-                    ListStyle::Bullet => {
-                        write!(f, "{:first_line_indent$}* {line}", "")?;
-                    }
-                    ListStyle::Enumerate => {
-                        write!(
-                            f,
-                            "{:first_line_indent$}{:>enumeration_padding$}. {line}",
-                            "", idx,
-                        )?;
-                    }
-                }
-            }
-            for line in lines {
-                writeln!(f)?;
-                write!(f, "{:other_line_indent$}{line}", "")?;
-            }
-            first_line_indent = first_line_of_element_indent;
-        }
-        Ok(())
-    }
-}
-
-impl FromIterator<String> for Description {
-    fn from_iter<T>(iter: T) -> Self
-    where
-        T: IntoIterator<Item = String>,
-    {
-        Self {
-            elements: iter.into_iter().collect(),
-            indent_mode: IndentMode::NoIndent,
-            list_style: ListStyle::NoList,
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::Description;
-    use crate::prelude::*;
-    use indoc::indoc;
-
-    #[test]
-    fn description_single_element() -> Result<()> {
-        let description = ["A B C".to_string()].into_iter().collect::<Description>();
-        verify_that!(description, displays_as(eq("A B C")))
-    }
-
-    #[test]
-    fn description_two_elements() -> Result<()> {
-        let description =
-            ["A B C".to_string(), "D E F".to_string()].into_iter().collect::<Description>();
-        verify_that!(description, displays_as(eq("A B C\nD E F")))
-    }
-
-    #[test]
-    fn description_indent_single_element() -> Result<()> {
-        let description = ["A B C".to_string()].into_iter().collect::<Description>().indent();
-        verify_that!(description, displays_as(eq("  A B C")))
-    }
-
-    #[test]
-    fn description_indent_two_elements() -> Result<()> {
-        let description = ["A B C".to_string(), "D E F".to_string()]
-            .into_iter()
-            .collect::<Description>()
-            .indent();
-        verify_that!(description, displays_as(eq("  A B C\n  D E F")))
-    }
-
-    #[test]
-    fn description_indent_two_elements_except_first_line() -> Result<()> {
-        let description = ["A B C".to_string(), "D E F".to_string()]
-            .into_iter()
-            .collect::<Description>()
-            .indent_except_first_line();
-        verify_that!(description, displays_as(eq("A B C\n  D E F")))
-    }
-
-    #[test]
-    fn description_indent_single_element_two_lines() -> Result<()> {
-        let description =
-            ["A B C\nD E F".to_string()].into_iter().collect::<Description>().indent();
-        verify_that!(description, displays_as(eq("  A B C\n  D E F")))
-    }
-
-    #[test]
-    fn description_indent_single_element_two_lines_except_first_line() -> Result<()> {
-        let description = ["A B C\nD E F".to_string()]
-            .into_iter()
-            .collect::<Description>()
-            .indent_except_first_line();
-        verify_that!(description, displays_as(eq("A B C\n  D E F")))
-    }
-
-    #[test]
-    fn description_bullet_single_element() -> Result<()> {
-        let description = ["A B C".to_string()].into_iter().collect::<Description>().bullet_list();
-        verify_that!(description, displays_as(eq("* A B C")))
-    }
-
-    #[test]
-    fn description_bullet_two_elements() -> Result<()> {
-        let description = ["A B C".to_string(), "D E F".to_string()]
-            .into_iter()
-            .collect::<Description>()
-            .bullet_list();
-        verify_that!(description, displays_as(eq("* A B C\n* D E F")))
-    }
-
-    #[test]
-    fn description_bullet_single_element_two_lines() -> Result<()> {
-        let description =
-            ["A B C\nD E F".to_string()].into_iter().collect::<Description>().bullet_list();
-        verify_that!(description, displays_as(eq("* A B C\n  D E F")))
-    }
-
-    #[test]
-    fn description_bullet_single_element_two_lines_indent_except_first_line() -> Result<()> {
-        let description = ["A B C\nD E F".to_string()]
-            .into_iter()
-            .collect::<Description>()
-            .bullet_list()
-            .indent_except_first_line();
-        verify_that!(description, displays_as(eq("* A B C\n    D E F")))
-    }
-
-    #[test]
-    fn description_bullet_two_elements_indent_except_first_line() -> Result<()> {
-        let description = ["A B C".to_string(), "D E F".to_string()]
-            .into_iter()
-            .collect::<Description>()
-            .bullet_list()
-            .indent_except_first_line();
-        verify_that!(description, displays_as(eq("* A B C\n  * D E F")))
-    }
-
-    #[test]
-    fn description_enumerate_single_element() -> Result<()> {
-        let description = ["A B C".to_string()].into_iter().collect::<Description>().enumerate();
-        verify_that!(description, displays_as(eq("0. A B C")))
-    }
-
-    #[test]
-    fn description_enumerate_two_elements() -> Result<()> {
-        let description = ["A B C".to_string(), "D E F".to_string()]
-            .into_iter()
-            .collect::<Description>()
-            .enumerate();
-        verify_that!(description, displays_as(eq("0. A B C\n1. D E F")))
-    }
-
-    #[test]
-    fn description_enumerate_single_element_two_lines() -> Result<()> {
-        let description =
-            ["A B C\nD E F".to_string()].into_iter().collect::<Description>().enumerate();
-        verify_that!(description, displays_as(eq("0. A B C\n   D E F")))
-    }
-
-    #[test]
-    fn description_enumerate_correct_indentation_with_large_index() -> Result<()> {
-        let description = ["A B C\nD E F"; 11]
-            .into_iter()
-            .map(str::to_string)
-            .collect::<Description>()
-            .enumerate();
-        verify_that!(
-            description,
-            displays_as(eq(indoc!(
-                "
-                 0. A B C
-                    D E F
-                 1. A B C
-                    D E F
-                 2. A B C
-                    D E F
-                 3. A B C
-                    D E F
-                 4. A B C
-                    D E F
-                 5. A B C
-                    D E F
-                 6. A B C
-                    D E F
-                 7. A B C
-                    D E F
-                 8. A B C
-                    D E F
-                 9. A B C
-                    D E F
-                10. A B C
-                    D E F"
-            )))
-        )
-    }
-}
diff --git a/src/matcher_support/edit_distance.rs b/src/matcher_support/edit_distance.rs
index 8469f42..8847bc9 100644
--- a/src/matcher_support/edit_distance.rs
+++ b/src/matcher_support/edit_distance.rs
@@ -19,7 +19,7 @@
 ///
 /// Increasing this limit increases the accuracy of [`edit_list`] while
 /// quadratically increasing its worst-case runtime.
-const MAX_DISTANCE: i32 = 25;
+const MAX_DISTANCE: i32 = 50;
 
 /// The difference between two inputs as produced by [`edit_list`].
 #[derive(Debug)]
@@ -533,7 +533,7 @@
 
     #[test]
     fn returns_unrelated_when_maximum_distance_exceeded() -> Result<()> {
-        let result = edit_list(0..=20, 20..40, Mode::Exact);
+        let result = edit_list(0..=50, 60..110, Mode::Exact);
         verify_that!(result, matches_pattern!(Difference::Unrelated))
     }
 
diff --git a/src/matcher_support/mod.rs b/src/matcher_support/mod.rs
index 8c30161..8a72ba4 100644
--- a/src/matcher_support/mod.rs
+++ b/src/matcher_support/mod.rs
@@ -19,7 +19,6 @@
 //! matchers.
 
 pub(crate) mod count_elements;
-pub mod description;
 pub(crate) mod edit_distance;
 pub(crate) mod summarize_diff;
 pub(crate) mod zipped_iterator;
diff --git a/src/matcher_support/summarize_diff.rs b/src/matcher_support/summarize_diff.rs
index a045282..cd4efa7 100644
--- a/src/matcher_support/summarize_diff.rs
+++ b/src/matcher_support/summarize_diff.rs
@@ -17,10 +17,7 @@
 use crate::matcher_support::edit_distance;
 #[rustversion::since(1.70)]
 use std::io::IsTerminal;
-use std::{
-    borrow::Cow,
-    fmt::{Display, Write},
-};
+use std::{borrow::Cow, fmt::Display};
 
 /// Returns a string describing how the expected and actual lines differ.
 ///
@@ -43,13 +40,10 @@
     }
     match edit_distance::edit_list(actual_debug.lines(), expected_debug.lines(), diff_mode) {
         edit_distance::Difference::Equal => "No difference found between debug strings.".into(),
-        edit_distance::Difference::Editable(edit_list) => format!(
-            "\nDifference({} / {}):{}",
-            LineStyle::extra_actual_style().style("actual"),
-            LineStyle::extra_expected_style().style("expected"),
-            edit_list.into_iter().collect::<BufferedSummary>(),
-        )
-        .into(),
+        edit_distance::Difference::Editable(edit_list) => {
+            format!("\n{}{}", summary_header(), edit_list.into_iter().collect::<BufferedSummary>(),)
+                .into()
+        }
         edit_distance::Difference::Unrelated => "".into(),
     }
 }
@@ -79,69 +73,125 @@
         edit_distance::Difference::Equal => "No difference found between debug strings.".into(),
         edit_distance::Difference::Editable(mut edit_list) => {
             edit_list.reverse();
-            format!(
-                "\nDifference({} / {}):{}",
-                LineStyle::extra_actual_style().style("actual"),
-                LineStyle::extra_expected_style().style("expected"),
-                edit_list.into_iter().collect::<BufferedSummary>(),
-            )
-            .into()
+            format!("\n{}{}", summary_header(), edit_list.into_iter().collect::<BufferedSummary>(),)
+                .into()
         }
         edit_distance::Difference::Unrelated => "".into(),
     }
 }
 
+// Produces the header, with or without coloring depending on
+// stdout_supports_color()
+fn summary_header() -> Cow<'static, str> {
+    if stdout_supports_color() {
+        format!(
+            "Difference(-{ACTUAL_ONLY_STYLE}actual{RESET_ALL} / +{EXPECTED_ONLY_STYLE}expected{RESET_ALL}):"
+        ).into()
+    } else {
+        "Difference(-actual / +expected):".into()
+    }
+}
+
 // Aggregator collecting the lines to be printed in the difference summary.
 //
 // This is buffered in order to allow a future line to potentially impact how
 // the current line would be printed.
+#[derive(Default)]
 struct BufferedSummary<'a> {
-    summary: String,
+    summary: SummaryBuilder,
     buffer: Buffer<'a>,
 }
 
 impl<'a> BufferedSummary<'a> {
     // Appends a new line which is common to both actual and expected.
     fn feed_common_lines(&mut self, common_line: &'a str) {
-        let Buffer::CommonLineBuffer(ref mut common_lines) = self.buffer;
-        common_lines.push(common_line);
+        if let Buffer::CommonLineBuffer(ref mut common_lines) = self.buffer {
+            common_lines.push(common_line);
+        } else {
+            self.flush_buffer();
+            self.buffer = Buffer::CommonLineBuffer(vec![common_line]);
+        }
     }
 
     // Appends a new line which is found only in the actual string.
     fn feed_extra_actual(&mut self, extra_actual: &'a str) {
-        self.buffer.flush(&mut self.summary).unwrap();
-        write!(&mut self.summary, "\n{}", LineStyle::extra_actual_style().style(extra_actual))
-            .unwrap();
+        if let Buffer::ExtraExpectedLineChunk(extra_expected) = self.buffer {
+            self.print_inline_diffs(extra_actual, extra_expected);
+            self.buffer = Buffer::Empty;
+        } else {
+            self.flush_buffer();
+            self.buffer = Buffer::ExtraActualLineChunk(extra_actual);
+        }
     }
 
     // Appends a new line which is found only in the expected string.
-    fn feed_extra_expected(&mut self, extra_expected: &str) {
-        self.flush_buffer();
-        write!(&mut self.summary, "\n{}", LineStyle::extra_expected_style().style(extra_expected))
-            .unwrap();
+    fn feed_extra_expected(&mut self, extra_expected: &'a str) {
+        if let Buffer::ExtraActualLineChunk(extra_actual) = self.buffer {
+            self.print_inline_diffs(extra_actual, extra_expected);
+            self.buffer = Buffer::Empty;
+        } else {
+            self.flush_buffer();
+            self.buffer = Buffer::ExtraExpectedLineChunk(extra_expected);
+        }
     }
 
     // Appends a comment for the additional line at the start or the end of the
     // actual string which should be omitted.
     fn feed_additional_actual(&mut self) {
         self.flush_buffer();
-        write!(
-            &mut self.summary,
-            "\n{}",
-            LineStyle::comment_style().style("<---- remaining lines omitted ---->")
-        )
-        .unwrap();
+        self.summary.new_line();
+        self.summary.push_str_as_comment("<---- remaining lines omitted ---->");
     }
 
     fn flush_buffer(&mut self) {
-        self.buffer.flush(&mut self.summary).unwrap();
+        self.buffer.flush(&mut self.summary);
+    }
+
+    fn print_inline_diffs(&mut self, actual_line: &str, expected_line: &str) {
+        let line_edits = edit_distance::edit_list(
+            actual_line.chars(),
+            expected_line.chars(),
+            edit_distance::Mode::Exact,
+        );
+
+        if let edit_distance::Difference::Editable(edit_list) = line_edits {
+            let mut actual_summary = SummaryBuilder::default();
+            actual_summary.new_line_for_actual();
+            let mut expected_summary = SummaryBuilder::default();
+            expected_summary.new_line_for_expected();
+            for edit in &edit_list {
+                match edit {
+                    edit_distance::Edit::ExtraActual(c) => actual_summary.push_actual_only(*c),
+                    edit_distance::Edit::ExtraExpected(c) => {
+                        expected_summary.push_expected_only(*c)
+                    }
+                    edit_distance::Edit::Both(c) => {
+                        actual_summary.push_actual_with_match(*c);
+                        expected_summary.push_expected_with_match(*c);
+                    }
+                    edit_distance::Edit::AdditionalActual => {
+                        // Calling edit_distance::edit_list(_, _, Mode::Exact) should never return
+                        // this enum
+                        panic!("This should not happen. This is a bug in gtest_rust")
+                    }
+                }
+            }
+            actual_summary.reset_ansi();
+            expected_summary.reset_ansi();
+            self.summary.push_str(&actual_summary.summary);
+            self.summary.push_str(&expected_summary.summary);
+        } else {
+            self.summary.new_line_for_actual();
+            self.summary.push_str_actual_only(actual_line);
+            self.summary.new_line_for_expected();
+            self.summary.push_str_expected_only(expected_line);
+        }
     }
 }
 
 impl<'a> FromIterator<edit_distance::Edit<&'a str>> for BufferedSummary<'a> {
     fn from_iter<T: IntoIterator<Item = edit_distance::Edit<&'a str>>>(iter: T) -> Self {
-        let mut buffered_summary =
-            BufferedSummary { summary: String::new(), buffer: Buffer::CommonLineBuffer(vec![]) };
+        let mut buffered_summary = BufferedSummary::default();
         for edit in iter {
             match edit {
                 edit_distance::Edit::Both(same) => {
@@ -159,6 +209,7 @@
             };
         }
         buffered_summary.flush_buffer();
+        buffered_summary.summary.reset_ansi();
 
         buffered_summary
     }
@@ -166,120 +217,80 @@
 
 impl<'a> Display for BufferedSummary<'a> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        if !matches!(self.buffer, Buffer::CommonLineBuffer(ref b) if b.is_empty()) {
+        if !matches!(self.buffer, Buffer::Empty) {
             panic!("Buffer is not empty. This is a bug in gtest_rust.")
         }
-        self.summary.fmt(f)
+        if !self.summary.last_ansi_style.is_empty() {
+            panic!("ANSI style has not been reset. This is a bug in gtest_rust.")
+        }
+        self.summary.summary.fmt(f)
     }
 }
 
-// This needs to be an enum as there will be in a follow-up PR new types of
-// buffer, most likely actual and expected lines, to be compared with expected
-// and actual lines for line to line comparison.
 enum Buffer<'a> {
+    Empty,
     CommonLineBuffer(Vec<&'a str>),
+    ExtraActualLineChunk(&'a str),
+    ExtraExpectedLineChunk(&'a str),
 }
 
 impl<'a> Buffer<'a> {
-    fn flush(&mut self, writer: impl std::fmt::Write) -> std::fmt::Result {
+    fn flush(&mut self, summary: &mut SummaryBuilder) {
         match self {
+            Buffer::Empty => {}
             Buffer::CommonLineBuffer(common_lines) => {
-                Self::flush_common_lines(std::mem::take(common_lines), writer)?
+                Self::flush_common_lines(std::mem::take(common_lines), summary);
+            }
+            Buffer::ExtraActualLineChunk(extra_actual) => {
+                summary.new_line_for_actual();
+                summary.push_str_actual_only(extra_actual);
+            }
+            Buffer::ExtraExpectedLineChunk(extra_expected) => {
+                summary.new_line_for_expected();
+                summary.push_str_expected_only(extra_expected);
             }
         };
-        Ok(())
+        *self = Buffer::Empty;
     }
 
-    fn flush_common_lines(
-        common_lines: Vec<&'a str>,
-        mut writer: impl std::fmt::Write,
-    ) -> std::fmt::Result {
+    fn flush_common_lines(common_lines: Vec<&'a str>, summary: &mut SummaryBuilder) {
         // The number of the lines kept before and after the compressed lines.
         const COMMON_LINES_CONTEXT_SIZE: usize = 2;
 
         if common_lines.len() <= 2 * COMMON_LINES_CONTEXT_SIZE + 1 {
             for line in common_lines {
-                write!(writer, "\n{}", LineStyle::unchanged_style().style(line))?;
+                summary.new_line();
+                summary.push_str(line);
             }
-            return Ok(());
+            return;
         }
 
         let start_context = &common_lines[0..COMMON_LINES_CONTEXT_SIZE];
 
         for line in start_context {
-            write!(writer, "\n{}", LineStyle::unchanged_style().style(line))?;
+            summary.new_line();
+            summary.push_str(line);
         }
 
-        write!(
-            writer,
-            "\n{}",
-            LineStyle::comment_style().style(&format!(
-                "<---- {} common lines omitted ---->",
-                common_lines.len() - 2 * COMMON_LINES_CONTEXT_SIZE
-            )),
-        )?;
+        summary.new_line();
+        summary.push_str_as_comment(&format!(
+            "<---- {} common lines omitted ---->",
+            common_lines.len() - 2 * COMMON_LINES_CONTEXT_SIZE,
+        ));
 
         let end_context =
             &common_lines[common_lines.len() - COMMON_LINES_CONTEXT_SIZE..common_lines.len()];
 
         for line in end_context {
-            write!(writer, "\n{}", LineStyle::unchanged_style().style(line))?;
+            summary.new_line();
+            summary.push_str(line);
         }
-        Ok(())
     }
 }
 
-// Use ANSI code to enable styling on the summary lines.
-//
-// See https://en.wikipedia.org/wiki/ANSI_escape_code.
-struct LineStyle {
-    ansi_prefix: &'static str,
-    ansi_suffix: &'static str,
-    header: char,
-}
-
-impl LineStyle {
-    // Font in red and bold
-    fn extra_actual_style() -> Self {
-        Self { ansi_prefix: "\x1B[1;31m", ansi_suffix: "\x1B[0m", header: '-' }
-    }
-
-    // Font in green and bold
-    fn extra_expected_style() -> Self {
-        Self { ansi_prefix: "\x1B[1;32m", ansi_suffix: "\x1B[0m", header: '+' }
-    }
-
-    // Font in italic
-    fn comment_style() -> Self {
-        Self { ansi_prefix: "\x1B[3m", ansi_suffix: "\x1B[0m", header: ' ' }
-    }
-
-    // No ansi styling
-    fn unchanged_style() -> Self {
-        Self { ansi_prefix: "", ansi_suffix: "", header: ' ' }
-    }
-
-    fn style(self, line: &str) -> StyledLine<'_> {
-        StyledLine { style: self, line }
-    }
-}
-
-struct StyledLine<'a> {
-    style: LineStyle,
-    line: &'a str,
-}
-
-impl<'a> Display for StyledLine<'a> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        if stdout_supports_color() {
-            write!(
-                f,
-                "{}{}{}{}",
-                self.style.header, self.style.ansi_prefix, self.line, self.style.ansi_suffix
-            )
-        } else {
-            write!(f, "{}{}", self.style.header, self.line)
-        }
+impl<'a> Default for Buffer<'a> {
+    fn default() -> Self {
+        Self::Empty
     }
 }
 
@@ -301,11 +312,107 @@
     std::env::var(var).map(|s| !s.is_empty()).unwrap_or(false)
 }
 
+// Font in italic
+const COMMENT_STYLE: &str = "\x1B[3m";
+// Font in green and bold
+const EXPECTED_ONLY_STYLE: &str = "\x1B[1;32m";
+// Font in red and bold
+const ACTUAL_ONLY_STYLE: &str = "\x1B[1;31m";
+// Font in green onlyh
+const EXPECTED_WITH_MATCH_STYLE: &str = "\x1B[32m";
+// Font in red only
+const ACTUAL_WITH_MATCH_STYLE: &str = "\x1B[31m";
+// Reset all ANSI formatting
+const RESET_ALL: &str = "\x1B[0m";
+
+#[derive(Default)]
+struct SummaryBuilder {
+    summary: String,
+    last_ansi_style: &'static str,
+}
+
+impl SummaryBuilder {
+    fn push_str(&mut self, element: &str) {
+        self.reset_ansi();
+        self.summary.push_str(element);
+    }
+
+    fn push_str_as_comment(&mut self, element: &str) {
+        self.set_ansi(COMMENT_STYLE);
+        self.summary.push_str(element);
+    }
+
+    fn push_str_actual_only(&mut self, element: &str) {
+        self.set_ansi(ACTUAL_ONLY_STYLE);
+        self.summary.push_str(element);
+    }
+
+    fn push_str_expected_only(&mut self, element: &str) {
+        self.set_ansi(EXPECTED_ONLY_STYLE);
+        self.summary.push_str(element);
+    }
+
+    fn push_actual_only(&mut self, element: char) {
+        self.set_ansi(ACTUAL_ONLY_STYLE);
+        self.summary.push(element);
+    }
+
+    fn push_expected_only(&mut self, element: char) {
+        self.set_ansi(EXPECTED_ONLY_STYLE);
+        self.summary.push(element);
+    }
+
+    fn push_actual_with_match(&mut self, element: char) {
+        self.set_ansi(ACTUAL_WITH_MATCH_STYLE);
+        self.summary.push(element);
+    }
+
+    fn push_expected_with_match(&mut self, element: char) {
+        self.set_ansi(EXPECTED_WITH_MATCH_STYLE);
+        self.summary.push(element);
+    }
+
+    fn new_line(&mut self) {
+        self.reset_ansi();
+        self.summary.push_str("\n ");
+    }
+
+    fn new_line_for_actual(&mut self) {
+        self.reset_ansi();
+        self.summary.push_str("\n-");
+    }
+
+    fn new_line_for_expected(&mut self) {
+        self.reset_ansi();
+        self.summary.push_str("\n+");
+    }
+
+    fn reset_ansi(&mut self) {
+        if !self.last_ansi_style.is_empty() && stdout_supports_color() {
+            self.summary.push_str(RESET_ALL);
+            self.last_ansi_style = "";
+        }
+    }
+
+    fn set_ansi(&mut self, ansi_style: &'static str) {
+        if !stdout_supports_color() || self.last_ansi_style == ansi_style {
+            return;
+        }
+        if !self.last_ansi_style.is_empty() {
+            self.summary.push_str(RESET_ALL);
+        }
+        self.summary.push_str(ansi_style);
+        self.last_ansi_style = ansi_style;
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
     use crate::{matcher_support::edit_distance::Mode, prelude::*};
     use indoc::indoc;
+    use serial_test::{parallel, serial};
+    use std::fmt::Write;
 
     // Make a long text with each element of the iterator on one line.
     // `collection` must contains at least one element.
@@ -320,11 +427,13 @@
     }
 
     #[test]
+    #[parallel]
     fn create_diff_smaller_than_one_line() -> Result<()> {
         verify_that!(create_diff("One", "Two", Mode::Exact), eq(""))
     }
 
     #[test]
+    #[parallel]
     fn create_diff_exact_same() -> Result<()> {
         let expected = indoc! {"
             One
@@ -341,14 +450,47 @@
     }
 
     #[test]
+    #[parallel]
+    fn create_diff_multiline_diff() -> Result<()> {
+        let expected = indoc! {"
+            prefix
+            Actual#1
+            Actual#2
+            Actual#3
+            suffix"};
+        let actual = indoc! {"
+            prefix
+            Expected@one
+            Expected@two
+            suffix"};
+        // TODO: It would be better to have all the Actual together followed by all the
+        // Expected together.
+        verify_that!(
+            create_diff(expected, actual, Mode::Exact),
+            eq(indoc!(
+                "
+
+                Difference(-actual / +expected):
+                 prefix
+                -Actual#1
+                +Expected@one
+                -Actual#2
+                +Expected@two
+                -Actual#3
+                 suffix"
+            ))
+        )
+    }
+
+    #[test]
+    #[parallel]
     fn create_diff_exact_unrelated() -> Result<()> {
         verify_that!(create_diff(&build_text(1..500), &build_text(501..1000), Mode::Exact), eq(""))
     }
 
     #[test]
-    fn create_diff_exact_small_difference_no_color() -> Result<()> {
-        std::env::set_var("NO_COLOR", "1");
-
+    #[parallel]
+    fn create_diff_exact_small_difference() -> Result<()> {
         verify_that!(
             create_diff(&build_text(1..50), &build_text(1..51), Mode::Exact),
             eq(indoc! {
@@ -364,4 +506,74 @@
             })
         )
     }
+
+    // Test with color enabled.
+
+    struct ForceColor;
+
+    fn force_color() -> ForceColor {
+        std::env::set_var("FORCE_COLOR", "1");
+        std::env::remove_var("NO_COLOR");
+        ForceColor
+    }
+
+    impl Drop for ForceColor {
+        fn drop(&mut self) {
+            std::env::remove_var("FORCE_COLOR");
+            std::env::set_var("NO_COLOR", "1");
+        }
+    }
+
+    #[test]
+    #[serial]
+    fn create_diff_exact_small_difference_with_color() -> Result<()> {
+        let _keep = force_color();
+
+        verify_that!(
+            create_diff(&build_text(1..50), &build_text(1..51), Mode::Exact),
+            eq(indoc! {
+                "
+
+                Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
+                 1
+                 2
+                 \x1B[3m<---- 45 common lines omitted ---->\x1B[0m
+                 48
+                 49
+                +\x1B[1;32m50\x1B[0m"
+            })
+        )
+    }
+
+    #[test]
+    #[serial]
+    fn create_diff_exact_difference_with_inline_color() -> Result<()> {
+        let _keep = force_color();
+        let actual = indoc!(
+            "There is a home in Nouvelle Orleans
+            They say, it is the rising sons
+            And it has been the ruin of many a po'boy"
+        );
+
+        let expected = indoc!(
+            "There is a house way down in New Orleans
+            They call the rising sun
+            And it has been the ruin of many a poor boy"
+        );
+
+        verify_that!(
+            create_diff(actual, expected, Mode::Exact),
+            eq(indoc! {
+                "
+
+                Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
+                -\x1B[31mThere is a ho\x1B[0m\x1B[1;31mm\x1B[0m\x1B[31me in N\x1B[0m\x1B[1;31mouv\x1B[0m\x1B[31me\x1B[0m\x1B[1;31mlle\x1B[0m\x1B[31m Orleans\x1B[0m
+                +\x1B[32mThere is a ho\x1B[0m\x1B[1;32mus\x1B[0m\x1B[32me \x1B[0m\x1B[1;32mway down \x1B[0m\x1B[32min Ne\x1B[0m\x1B[1;32mw\x1B[0m\x1B[32m Orleans\x1B[0m
+                -\x1B[31mThey \x1B[0m\x1B[1;31ms\x1B[0m\x1B[31ma\x1B[0m\x1B[1;31my,\x1B[0m\x1B[31m \x1B[0m\x1B[1;31mi\x1B[0m\x1B[31mt\x1B[0m\x1B[1;31m is t\x1B[0m\x1B[31mhe rising s\x1B[0m\x1B[1;31mo\x1B[0m\x1B[31mn\x1B[0m\x1B[1;31ms\x1B[0m
+                +\x1B[32mThey \x1B[0m\x1B[1;32mc\x1B[0m\x1B[32ma\x1B[0m\x1B[1;32mll\x1B[0m\x1B[32m the rising s\x1B[0m\x1B[1;32mu\x1B[0m\x1B[32mn\x1B[0m
+                -\x1B[31mAnd it has been the ruin of many a po\x1B[0m\x1B[1;31m'\x1B[0m\x1B[31mboy\x1B[0m
+                +\x1B[32mAnd it has been the ruin of many a po\x1B[0m\x1B[1;32mor \x1B[0m\x1B[32mboy\x1B[0m"
+            })
+        )
+    }
 }
diff --git a/src/matchers/all_matcher.rs b/src/matchers/all_matcher.rs
index cc959c7..f2e6d06 100644
--- a/src/matchers/all_matcher.rs
+++ b/src/matchers/all_matcher.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // There are no visible documentation elements in this module; the declarative
-// macro is documented at the top level.
+// macro is documented in the matcher module.
 #![doc(hidden)]
 
 /// Matches a value which all of the given matchers match.
@@ -51,9 +51,10 @@
 ///
 /// Assertion failure messages are not guaranteed to be identical, however.
 #[macro_export]
-macro_rules! all {
+#[doc(hidden)]
+macro_rules! __all {
     ($($matcher:expr),* $(,)?) => {{
-        use $crate::matchers::all_matcher::internal::AllMatcher;
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::AllMatcher;
         AllMatcher::new([$(Box::new($matcher)),*])
     }}
 }
@@ -63,8 +64,8 @@
 /// For internal use only. API stablility is not guaranteed!
 #[doc(hidden)]
 pub mod internal {
+    use crate::description::Description;
     use crate::matcher::{Matcher, MatcherResult};
-    use crate::matcher_support::description::Description;
     use crate::matchers::anything;
     use std::fmt::Debug;
 
@@ -101,7 +102,7 @@
             MatcherResult::Match
         }
 
-        fn explain_match(&self, actual: &Self::ActualT) -> String {
+        fn explain_match(&self, actual: &Self::ActualT) -> Description {
             match N {
                 0 => anything::<T>().explain_match(actual),
                 1 => self.components[0].explain_match(actual),
@@ -110,36 +111,37 @@
                         .components
                         .iter()
                         .filter(|component| component.matches(actual).is_no_match())
-                        .map(|component| component.explain_match(actual))
-                        .collect::<Description>();
+                        .collect::<Vec<_>>();
+
                     if failures.len() == 1 {
-                        format!("{}", failures)
+                        failures[0].explain_match(actual)
                     } else {
-                        format!("{}", failures.bullet_list().indent_except_first_line())
+                        Description::new()
+                            .collect(
+                                failures
+                                    .into_iter()
+                                    .map(|component| component.explain_match(actual)),
+                            )
+                            .bullet_list()
                     }
                 }
             }
         }
 
-        fn describe(&self, matcher_result: MatcherResult) -> String {
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
             match N {
                 0 => anything::<T>().describe(matcher_result),
                 1 => self.components[0].describe(matcher_result),
                 _ => {
-                    let properties = self
-                        .components
-                        .iter()
-                        .map(|m| m.describe(matcher_result))
-                        .collect::<Description>()
-                        .bullet_list()
-                        .indent();
-                    format!(
-                        "{}:\n{properties}",
-                        if matcher_result.into() {
-                            "has all the following properties"
-                        } else {
-                            "has at least one of the following properties"
-                        }
+                    let header = if matcher_result.into() {
+                        "has all the following properties:"
+                    } else {
+                        "has at least one of the following properties:"
+                    };
+                    Description::new().text(header).nested(
+                        Description::new()
+                            .bullet_list()
+                            .collect(self.components.iter().map(|m| m.describe(matcher_result))),
                     )
                 }
             }
@@ -162,12 +164,12 @@
 
         verify_that!(
             matcher.describe(MatcherResult::Match),
-            eq(indoc!(
+            displays_as(eq(indoc!(
                 "
                 has all the following properties:
                   * starts with prefix \"A\"
                   * ends with suffix \"string\""
-            ))
+            )))
         )
     }
 
@@ -176,7 +178,10 @@
         let first_matcher = starts_with("A");
         let matcher: internal::AllMatcher<String, 1> = all!(first_matcher);
 
-        verify_that!(matcher.describe(MatcherResult::Match), eq("starts with prefix \"A\""))
+        verify_that!(
+            matcher.describe(MatcherResult::Match),
+            displays_as(eq("starts with prefix \"A\""))
+        )
     }
 
     #[test]
diff --git a/src/matchers/any_matcher.rs b/src/matchers/any_matcher.rs
index 82c8910..95d53fe 100644
--- a/src/matchers/any_matcher.rs
+++ b/src/matchers/any_matcher.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // There are no visible documentation elements in this module; the declarative
-// macro is documented at the top level.
+// macro is documented in the matchers module.
 #![doc(hidden)]
 
 /// Matches a value which at least one of the given matchers match.
@@ -53,9 +53,10 @@
 ///
 /// Assertion failure messages are not guaranteed to be identical, however.
 #[macro_export]
-macro_rules! any {
+#[doc(hidden)]
+macro_rules! __any {
     ($($matcher:expr),* $(,)?) => {{
-        use $crate::matchers::any_matcher::internal::AnyMatcher;
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::AnyMatcher;
         AnyMatcher::new([$(Box::new($matcher)),*])
     }}
 }
@@ -65,8 +66,8 @@
 /// For internal use only. API stablility is not guaranteed!
 #[doc(hidden)]
 pub mod internal {
+    use crate::description::Description;
     use crate::matcher::{Matcher, MatcherResult};
-    use crate::matcher_support::description::Description;
     use crate::matchers::anything;
     use std::fmt::Debug;
 
@@ -95,27 +96,33 @@
             MatcherResult::from(self.components.iter().any(|c| c.matches(actual).is_match()))
         }
 
-        fn explain_match(&self, actual: &Self::ActualT) -> String {
+        fn explain_match(&self, actual: &Self::ActualT) -> Description {
             match N {
-                0 => format!("which {}", anything::<T>().describe(MatcherResult::NoMatch)),
+                0 => format!("which {}", anything::<T>().describe(MatcherResult::NoMatch)).into(),
                 1 => self.components[0].explain_match(actual),
                 _ => {
                     let failures = self
                         .components
                         .iter()
                         .filter(|component| component.matches(actual).is_no_match())
-                        .map(|component| component.explain_match(actual))
-                        .collect::<Description>();
+                        .collect::<Vec<_>>();
+
                     if failures.len() == 1 {
-                        format!("{}", failures)
+                        failures[0].explain_match(actual)
                     } else {
-                        format!("{}", failures.bullet_list().indent_except_first_line())
+                        Description::new()
+                            .collect(
+                                failures
+                                    .into_iter()
+                                    .map(|component| component.explain_match(actual)),
+                            )
+                            .bullet_list()
                     }
                 }
             }
         }
 
-        fn describe(&self, matcher_result: MatcherResult) -> String {
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
             match N {
                 0 => anything::<T>().describe(matcher_result),
                 1 => self.components[0].describe(matcher_result),
@@ -135,6 +142,7 @@
                             "has none of the following properties"
                         }
                     )
+                    .into()
                 }
             }
         }
@@ -156,12 +164,12 @@
 
         verify_that!(
             matcher.describe(MatcherResult::Match),
-            eq(indoc!(
+            displays_as(eq(indoc!(
                 "
                 has at least one of the following properties:
                   * starts with prefix \"A\"
                   * ends with suffix \"string\""
-            ))
+            )))
         )
     }
 
@@ -170,7 +178,10 @@
         let first_matcher = starts_with("A");
         let matcher: internal::AnyMatcher<String, 1> = any!(first_matcher);
 
-        verify_that!(matcher.describe(MatcherResult::Match), eq("starts with prefix \"A\""))
+        verify_that!(
+            matcher.describe(MatcherResult::Match),
+            displays_as(eq("starts with prefix \"A\""))
+        )
     }
 
     #[test]
diff --git a/src/matchers/anything_matcher.rs b/src/matchers/anything_matcher.rs
index 82de460..36be478 100644
--- a/src/matchers/anything_matcher.rs
+++ b/src/matchers/anything_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches anything. This matcher always succeeds.
@@ -42,10 +45,10 @@
         MatcherResult::Match
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
-            MatcherResult::Match => "is anything".to_string(),
-            MatcherResult::NoMatch => "never matches".to_string(),
+            MatcherResult::Match => "is anything".into(),
+            MatcherResult::NoMatch => "never matches".into(),
         }
     }
 }
diff --git a/src/matchers/char_count_matcher.rs b/src/matchers/char_count_matcher.rs
index a7765b4..70977d7 100644
--- a/src/matchers/char_count_matcher.rs
+++ b/src/matchers/char_count_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches a string whose number of Unicode scalars matches `expected`.
@@ -71,36 +74,36 @@
         self.expected.matches(&actual.as_ref().chars().count())
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
-            MatcherResult::Match => {
-                format!(
-                    "has character count, which {}",
-                    self.expected.describe(MatcherResult::Match)
-                )
-            }
-            MatcherResult::NoMatch => {
-                format!(
-                    "has character count, which {}",
-                    self.expected.describe(MatcherResult::NoMatch)
-                )
-            }
+            MatcherResult::Match => format!(
+                "has character count, which {}",
+                self.expected.describe(MatcherResult::Match)
+            )
+            .into(),
+            MatcherResult::NoMatch => format!(
+                "has character count, which {}",
+                self.expected.describe(MatcherResult::NoMatch)
+            )
+            .into(),
         }
     }
 
-    fn explain_match(&self, actual: &T) -> String {
+    fn explain_match(&self, actual: &T) -> Description {
         let actual_size = actual.as_ref().chars().count();
         format!(
             "which has character count {}, {}",
             actual_size,
             self.expected.explain_match(&actual_size)
         )
+        .into()
     }
 }
 
 #[cfg(test)]
 mod tests {
     use super::char_count;
+    use crate::description::Description;
     use crate::matcher::{Matcher, MatcherResult};
     use crate::prelude::*;
     use indoc::indoc;
@@ -135,11 +138,11 @@
                 false.into()
             }
 
-            fn describe(&self, _: MatcherResult) -> String {
+            fn describe(&self, _: MatcherResult) -> Description {
                 "called described".into()
             }
 
-            fn explain_match(&self, _: &T) -> String {
+            fn explain_match(&self, _: &T) -> Description {
                 "called explain_match".into()
             }
         }
diff --git a/src/matchers/conjunction_matcher.rs b/src/matchers/conjunction_matcher.rs
index 1ba59c3..dc50833 100644
--- a/src/matchers/conjunction_matcher.rs
+++ b/src/matchers/conjunction_matcher.rs
@@ -15,7 +15,10 @@
 // There are no visible documentation elements in this module.
 #![doc(hidden)]
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::fmt::Debug;
 
 /// Matcher created by [`Matcher::and`].
@@ -46,29 +49,24 @@
         }
     }
 
-    fn explain_match(&self, actual: &M1::ActualT) -> String {
+    fn explain_match(&self, actual: &M1::ActualT) -> Description {
         match (self.m1.matches(actual), self.m2.matches(actual)) {
-            (MatcherResult::Match, MatcherResult::Match) => {
-                format!(
-                    "{} and\n  {}",
-                    self.m1.explain_match(actual),
-                    self.m2.explain_match(actual)
-                )
-            }
+            (MatcherResult::Match, MatcherResult::Match) => Description::new()
+                .nested(self.m1.explain_match(actual))
+                .text("and")
+                .nested(self.m2.explain_match(actual)),
             (MatcherResult::NoMatch, MatcherResult::Match) => self.m1.explain_match(actual),
             (MatcherResult::Match, MatcherResult::NoMatch) => self.m2.explain_match(actual),
-            (MatcherResult::NoMatch, MatcherResult::NoMatch) => {
-                format!(
-                    "{} and\n  {}",
-                    self.m1.explain_match(actual),
-                    self.m2.explain_match(actual)
-                )
-            }
+            (MatcherResult::NoMatch, MatcherResult::NoMatch) => Description::new()
+                .nested(self.m1.explain_match(actual))
+                .text("and")
+                .nested(self.m2.explain_match(actual)),
         }
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         format!("{}, and {}", self.m1.describe(matcher_result), self.m2.describe(matcher_result))
+            .into()
     }
 }
 
@@ -124,8 +122,9 @@
                 Value of: 1
                 Expected: never matches, and never matches
                 Actual: 1,
-                  which is anything and
-                  which is anything
+                    which is anything
+                  and
+                    which is anything
                 "
             ))))
         )
diff --git a/src/matchers/container_eq_matcher.rs b/src/matchers/container_eq_matcher.rs
index f80cebf..d4f872c 100644
--- a/src/matchers/container_eq_matcher.rs
+++ b/src/matchers/container_eq_matcher.rs
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use crate::description::Description;
 use crate::matcher::{Matcher, MatcherResult};
 use std::fmt::Debug;
 use std::marker::PhantomData;
@@ -29,9 +30,14 @@
 ///   Unexpected: [4]
 /// ```
 ///
-/// The type of `expected` must implement `IntoIterator` with an `Item` which
-/// implements `PartialEq`. If the container type is a `Vec`, then the expected
-/// type may be a slice of the same element type. For example:
+/// The actual value must be a container such as a `Vec`, an array, or a
+/// dereferenced slice. More precisely, a shared borrow of the actual value must
+/// implement [`IntoIterator`] whose `Item` type implements
+/// [`PartialEq<ExpectedT>`], where `ExpectedT` is the element type of the
+/// expected value.
+///
+/// If the container type is a `Vec`, then the expected type may be a slice of
+/// the same element type. For example:
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -114,14 +120,14 @@
         (*actual == self.expected).into()
     }
 
-    fn explain_match(&self, actual: &ActualContainerT) -> String {
-        build_explanation(self.get_missing_items(actual), self.get_unexpected_items(actual))
+    fn explain_match(&self, actual: &ActualContainerT) -> Description {
+        build_explanation(self.get_missing_items(actual), self.get_unexpected_items(actual)).into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
-            MatcherResult::Match => format!("is equal to {:?}", self.expected),
-            MatcherResult::NoMatch => format!("isn't equal to {:?}", self.expected),
+            MatcherResult::Match => format!("is equal to {:?}", self.expected).into(),
+            MatcherResult::NoMatch => format!("isn't equal to {:?}", self.expected).into(),
         }
     }
 }
diff --git a/src/matchers/contains_matcher.rs b/src/matchers/contains_matcher.rs
index d714c88..1a27ce0 100644
--- a/src/matchers/contains_matcher.rs
+++ b/src/matchers/contains_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches an iterable type whose elements contain a value matched by `inner`.
@@ -101,33 +104,37 @@
         }
     }
 
-    fn explain_match(&self, actual: &Self::ActualT) -> String {
+    fn explain_match(&self, actual: &Self::ActualT) -> Description {
         let count = self.count_matches(actual);
         match (count, &self.count) {
-            (_, Some(_)) => format!("which contains {} matching elements", count),
-            (0, None) => "which does not contain a matching element".to_string(),
-            (_, None) => "which contains a matching element".to_string(),
+            (_, Some(_)) => format!("which contains {} matching elements", count).into(),
+            (0, None) => "which does not contain a matching element".into(),
+            (_, None) => "which contains a matching element".into(),
         }
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match (matcher_result, &self.count) {
             (MatcherResult::Match, Some(count)) => format!(
                 "contains n elements which {}\n  where n {}",
                 self.inner.describe(MatcherResult::Match),
                 count.describe(MatcherResult::Match)
-            ),
+            )
+            .into(),
             (MatcherResult::NoMatch, Some(count)) => format!(
                 "doesn't contain n elements which {}\n  where n {}",
                 self.inner.describe(MatcherResult::Match),
                 count.describe(MatcherResult::Match)
-            ),
+            )
+            .into(),
             (MatcherResult::Match, None) => format!(
                 "contains at least one element which {}",
                 self.inner.describe(MatcherResult::Match)
-            ),
+            )
+            .into(),
             (MatcherResult::NoMatch, None) => {
                 format!("contains no element which {}", self.inner.describe(MatcherResult::Match))
+                    .into()
             }
         }
     }
@@ -193,7 +200,7 @@
 
     #[test]
     fn contains_does_not_match_empty_slice() -> Result<()> {
-        let matcher = contains(eq(1));
+        let matcher = contains(eq::<i32, _>(1));
 
         let result = matcher.matches(&[]);
 
@@ -233,7 +240,7 @@
 
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq("contains at least one element which is equal to 1")
+            displays_as(eq("contains at least one element which is equal to 1"))
         )
     }
 
@@ -243,7 +250,7 @@
 
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq("contains n elements which is equal to 1\n  where n is equal to 2")
+            displays_as(eq("contains n elements which is equal to 1\n  where n is equal to 2"))
         )
     }
 
diff --git a/src/matchers/contains_regex_matcher.rs b/src/matchers/contains_regex_matcher.rs
index 8cc93a7..6f68168 100644
--- a/src/matchers/contains_regex_matcher.rs
+++ b/src/matchers/contains_regex_matcher.rs
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use crate::description::Description;
 use crate::matcher::{Matcher, MatcherResult};
 use regex::Regex;
 use std::fmt::Debug;
@@ -77,13 +78,13 @@
         self.regex.is_match(actual.as_ref()).into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
             MatcherResult::Match => {
-                format!("contains the regular expression {:#?}", self.regex.as_str())
+                format!("contains the regular expression {:#?}", self.regex.as_str()).into()
             }
             MatcherResult::NoMatch => {
-                format!("doesn't contain the regular expression {:#?}", self.regex.as_str())
+                format!("doesn't contain the regular expression {:#?}", self.regex.as_str()).into()
             }
         }
     }
@@ -142,7 +143,7 @@
 
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq("contains the regular expression \"\\n\"")
+            displays_as(eq("contains the regular expression \"\\n\""))
         )
     }
 }
diff --git a/src/matchers/disjunction_matcher.rs b/src/matchers/disjunction_matcher.rs
index 48bf226..dd56be2 100644
--- a/src/matchers/disjunction_matcher.rs
+++ b/src/matchers/disjunction_matcher.rs
@@ -15,7 +15,10 @@
 // There are no visible documentation elements in this module.
 #![doc(hidden)]
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::fmt::Debug;
 
 /// Matcher created by [`Matcher::or`].
@@ -46,12 +49,16 @@
         }
     }
 
-    fn explain_match(&self, actual: &M1::ActualT) -> String {
-        format!("{} and\n  {}", self.m1.explain_match(actual), self.m2.explain_match(actual))
+    fn explain_match(&self, actual: &M1::ActualT) -> Description {
+        Description::new()
+            .nested(self.m1.explain_match(actual))
+            .text("and")
+            .nested(self.m2.explain_match(actual))
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         format!("{}, or {}", self.m1.describe(matcher_result), self.m2.describe(matcher_result))
+            .into()
     }
 }
 
@@ -85,8 +92,9 @@
                 Value of: 1
                 Expected: never matches, or never matches
                 Actual: 1,
-                  which is anything and
-                  which is anything
+                    which is anything
+                  and
+                    which is anything
                 "
             ))))
         )
diff --git a/src/matchers/display_matcher.rs b/src/matchers/display_matcher.rs
index 628769b..51a5bff 100644
--- a/src/matchers/display_matcher.rs
+++ b/src/matchers/display_matcher.rs
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use crate::description::Description;
 use crate::matcher::{Matcher, MatcherResult};
 use std::fmt::{Debug, Display};
 use std::marker::PhantomData;
@@ -42,21 +43,22 @@
         self.inner.matches(&format!("{actual}"))
     }
 
-    fn explain_match(&self, actual: &T) -> String {
+    fn explain_match(&self, actual: &T) -> Description {
         format!("which displays as a string {}", self.inner.explain_match(&format!("{actual}")))
+            .into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
             MatcherResult::Match => {
                 format!("displays as a string which {}", self.inner.describe(MatcherResult::Match))
+                    .into()
             }
-            MatcherResult::NoMatch => {
-                format!(
-                    "doesn't display as a string which {}",
-                    self.inner.describe(MatcherResult::Match)
-                )
-            }
+            MatcherResult::NoMatch => format!(
+                "doesn't display as a string which {}",
+                self.inner.describe(MatcherResult::Match)
+            )
+            .into(),
         }
     }
 }
@@ -107,6 +109,7 @@
             result,
             err(displays_as(contains_substring(indoc!(
                 "
+                  Actual: \"123\\n234\",
                     which displays as a string which isn't equal to \"123\\n345\"
                     Difference(-actual / +expected):
                      123
diff --git a/src/matchers/each_matcher.rs b/src/matchers/each_matcher.rs
index ce61207..08a0742 100644
--- a/src/matchers/each_matcher.rs
+++ b/src/matchers/each_matcher.rs
@@ -12,14 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use crate::description::Description;
 use crate::matcher::{Matcher, MatcherResult};
-use crate::matcher_support::description::Description;
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches a container all of whose elements are matched by the matcher
 /// `inner`.
 ///
-/// `T` can be any container such that `&T` implements `IntoIterator`.
+/// `T` can be any container such that `&T` implements `IntoIterator`. This
+/// includes `Vec`, arrays, and (dereferenced) slices.
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -27,6 +28,10 @@
 /// # fn should_pass_1() -> Result<()> {
 /// let value = vec![1, 2, 3];
 /// verify_that!(value, each(gt(0)))?;  // Passes
+/// let array_value = [1, 2, 3];
+/// verify_that!(array_value, each(gt(0)))?;  // Passes
+/// let slice_value = &[1, 2, 3];
+/// verify_that!(*slice_value, each(gt(0)))?;  // Passes
 /// #     Ok(())
 /// # }
 /// # fn should_fail() -> Result<()> {
@@ -87,7 +92,7 @@
         MatcherResult::Match
     }
 
-    fn explain_match(&self, actual: &ActualT) -> String {
+    fn explain_match(&self, actual: &ActualT) -> Description {
         let mut non_matching_elements = Vec::new();
         for (index, element) in actual.into_iter().enumerate() {
             if self.inner.matches(element).is_no_match() {
@@ -95,11 +100,12 @@
             }
         }
         if non_matching_elements.is_empty() {
-            return format!("whose each element {}", self.inner.describe(MatcherResult::Match));
+            return format!("whose each element {}", self.inner.describe(MatcherResult::Match))
+                .into();
         }
         if non_matching_elements.len() == 1 {
             let (idx, element, explanation) = non_matching_elements.remove(0);
-            return format!("whose element #{idx} is {element:?}, {explanation}");
+            return format!("whose element #{idx} is {element:?}, {explanation}").into();
         }
 
         let failed_indexes = non_matching_elements
@@ -112,16 +118,18 @@
             .map(|&(_, element, ref explanation)| format!("{element:?}, {explanation}"))
             .collect::<Description>()
             .indent();
-        format!("whose elements {failed_indexes} don't match\n{element_explanations}")
+        format!("whose elements {failed_indexes} don't match\n{element_explanations}").into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
             MatcherResult::Match => {
                 format!("only contains elements that {}", self.inner.describe(MatcherResult::Match))
+                    .into()
             }
             MatcherResult::NoMatch => {
                 format!("contains no element that {}", self.inner.describe(MatcherResult::Match))
+                    .into()
             }
         }
     }
@@ -220,8 +228,8 @@
                 Expected: only contains elements that is greater than 1
                 Actual: [0, 1, 3],
                   whose elements #0, #1 don't match
-                  0, which is less than or equal to 1
-                  1, which is less than or equal to 1"
+                    0, which is less than or equal to 1
+                    1, which is less than or equal to 1"
             ))))
         )
     }
@@ -233,7 +241,6 @@
             result,
             err(displays_as(contains_substring(indoc!(
                 "
-                Value of: vec![vec! [1, 2], vec! [1]]
                 Expected: only contains elements that only contains elements that is equal to 1
                 Actual: [[1, 2], [1]],
                   whose element #0 is [1, 2], whose element #1 is 2, which isn't equal to 1"
diff --git a/src/matchers/elements_are_matcher.rs b/src/matchers/elements_are_matcher.rs
index 736bf47..3a4b5b2 100644
--- a/src/matchers/elements_are_matcher.rs
+++ b/src/matchers/elements_are_matcher.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // There are no visible documentation elements in this module; the declarative
-// macro is documented at the top level.
+// macro is documented in the matchers module.
 #![doc(hidden)]
 
 /// Matches a container's elements to each matcher in order.
@@ -28,8 +28,9 @@
 /// #    .unwrap();
 /// ```
 ///
-/// The actual value must be a container implementing [`IntoIterator`]. This
-/// includes standard containers, slices (when dereferenced) and arrays.
+/// The actual value must be a container such as a `Vec`, an array, or a
+/// dereferenced slice. More precisely, a shared borrow of the actual value must
+/// implement [`IntoIterator`].
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -50,7 +51,7 @@
 ///
 /// Note: This behavior is only possible in [`verify_that!`] macros. In any
 /// other cases, it is still necessary to use the
-/// [`elements_are!`][crate::elements_are] macro.
+/// [`elements_are!`][crate::matchers::elements_are] macro.
 ///
 /// ```compile_fail
 /// # use googletest::prelude::*;
@@ -69,7 +70,8 @@
 /// match against an iterator, use [`Iterator::collect`] to build a [`Vec`].
 ///
 /// Do not use this with unordered containers, since that will lead to flaky
-/// tests. Use [`unordered_elements_are!`][crate::unordered_elements_are]
+/// tests. Use
+/// [`unordered_elements_are!`][crate::matchers::unordered_elements_are]
 /// instead.
 ///
 /// [`IntoIterator`]: std::iter::IntoIterator
@@ -77,9 +79,10 @@
 /// [`Iterator::collect`]: std::iter::Iterator::collect
 /// [`Vec`]: std::vec::Vec
 #[macro_export]
-macro_rules! elements_are {
+#[doc(hidden)]
+macro_rules! __elements_are {
     ($($matcher:expr),* $(,)?) => {{
-        use $crate::matchers::elements_are_matcher::internal::ElementsAre;
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::ElementsAre;
         ElementsAre::new(vec![$(Box::new($matcher)),*])
     }}
 }
@@ -89,8 +92,8 @@
 /// **For internal use only. API stablility is not guaranteed!**
 #[doc(hidden)]
 pub mod internal {
+    use crate::description::Description;
     use crate::matcher::{Matcher, MatcherResult};
-    use crate::matcher_support::description::Description;
     use crate::matcher_support::zipped_iterator::zip;
     use std::{fmt::Debug, marker::PhantomData};
 
@@ -133,7 +136,7 @@
             }
         }
 
-        fn explain_match(&self, actual: &ContainerT) -> String {
+        fn explain_match(&self, actual: &ContainerT) -> Description {
             let actual_iterator = actual.into_iter();
             let mut zipped_iterator = zip(actual_iterator, self.elements.iter());
             let mut mismatches = Vec::new();
@@ -144,20 +147,20 @@
             }
             if mismatches.is_empty() {
                 if !zipped_iterator.has_size_mismatch() {
-                    "whose elements all match".to_string()
+                    "whose elements all match".into()
                 } else {
-                    format!("whose size is {}", zipped_iterator.left_size())
+                    format!("whose size is {}", zipped_iterator.left_size()).into()
                 }
             } else if mismatches.len() == 1 {
                 let mismatches = mismatches.into_iter().collect::<Description>();
-                format!("where {mismatches}")
+                format!("where {mismatches}").into()
             } else {
                 let mismatches = mismatches.into_iter().collect::<Description>();
-                format!("where:\n{}", mismatches.bullet_list().indent())
+                format!("where:\n{}", mismatches.bullet_list().indent()).into()
             }
         }
 
-        fn describe(&self, matcher_result: MatcherResult) -> String {
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
             format!(
                 "{} elements:\n{}",
                 if matcher_result.into() { "has" } else { "doesn't have" },
@@ -169,6 +172,7 @@
                     .enumerate()
                     .indent()
             )
+            .into()
         }
     }
 }
diff --git a/src/matchers/empty_matcher.rs b/src/matchers/empty_matcher.rs
index afefcb7..11cb675 100644
--- a/src/matchers/empty_matcher.rs
+++ b/src/matchers/empty_matcher.rs
@@ -12,12 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches an empty container.
 ///
-/// `T` can be any container such that `&T` implements `IntoIterator`.
+/// `T` can be any container such that `&T` implements `IntoIterator`. For
+/// instance, `T` can be a common container like `Vec` and
+/// [`HashSet`][std::collections::HashSet].
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -66,8 +71,8 @@
         actual.into_iter().next().is_none().into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
-        if matcher_result.into() { "is empty" } else { "isn't empty" }.to_string()
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
+        if matcher_result.into() { "is empty" } else { "isn't empty" }.into()
     }
 }
 
diff --git a/src/matchers/eq_deref_of_matcher.rs b/src/matchers/eq_deref_of_matcher.rs
index 1540905..320aafb 100644
--- a/src/matchers/eq_deref_of_matcher.rs
+++ b/src/matchers/eq_deref_of_matcher.rs
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 use crate::{
+    description::Description,
     matcher::{Matcher, MatcherResult},
     matcher_support::{edit_distance, summarize_diff::create_diff},
 };
@@ -76,14 +77,14 @@
         (self.expected.deref() == actual).into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
-            MatcherResult::Match => format!("is equal to {:?}", self.expected),
-            MatcherResult::NoMatch => format!("isn't equal to {:?}", self.expected),
+            MatcherResult::Match => format!("is equal to {:?}", self.expected).into(),
+            MatcherResult::NoMatch => format!("isn't equal to {:?}", self.expected).into(),
         }
     }
 
-    fn explain_match(&self, actual: &ActualT) -> String {
+    fn explain_match(&self, actual: &ActualT) -> Description {
         format!(
             "which {}{}",
             &self.describe(self.matches(actual)),
@@ -93,6 +94,7 @@
                 edit_distance::Mode::Exact,
             )
         )
+        .into()
     }
 }
 
@@ -138,13 +140,13 @@
             "
             Actual: Strukt { int: 123, string: \"something\" },
               which isn't equal to Strukt { int: 321, string: \"someone\" }
-            Difference(-actual / +expected):
-             Strukt {
-            -    int: 123,
-            +    int: 321,
-            -    string: \"something\",
-            +    string: \"someone\",
-             }
+              Difference(-actual / +expected):
+               Strukt {
+              -    int: 123,
+              +    int: 321,
+              -    string: \"something\",
+              +    string: \"someone\",
+               }
             "})))
         )
     }
diff --git a/src/matchers/eq_matcher.rs b/src/matchers/eq_matcher.rs
index 42684c9..985307f 100644
--- a/src/matchers/eq_matcher.rs
+++ b/src/matchers/eq_matcher.rs
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use crate::description::Description;
 use crate::matcher::{Matcher, MatcherResult};
 use crate::matcher_support::edit_distance;
 use crate::matcher_support::summarize_diff::create_diff;
@@ -82,21 +83,21 @@
     phantom: PhantomData<A>,
 }
 
-impl<A: Debug + ?Sized, T: PartialEq<A> + Debug> Matcher for EqMatcher<A, T> {
+impl<T: Debug, A: Debug + ?Sized + PartialEq<T>> Matcher for EqMatcher<A, T> {
     type ActualT = A;
 
     fn matches(&self, actual: &A) -> MatcherResult {
-        (self.expected == *actual).into()
+        (*actual == self.expected).into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
-            MatcherResult::Match => format!("is equal to {:?}", self.expected),
-            MatcherResult::NoMatch => format!("isn't equal to {:?}", self.expected),
+            MatcherResult::Match => format!("is equal to {:?}", self.expected).into(),
+            MatcherResult::NoMatch => format!("isn't equal to {:?}", self.expected).into(),
         }
     }
 
-    fn explain_match(&self, actual: &A) -> String {
+    fn explain_match(&self, actual: &A) -> Description {
         let expected_debug = format!("{:#?}", self.expected);
         let actual_debug = format!("{:#?}", actual);
         let description = self.describe(self.matches(actual));
@@ -117,7 +118,7 @@
             create_diff(&actual_debug, &expected_debug, edit_distance::Mode::Exact)
         };
 
-        format!("which {description}{diff}")
+        format!("which {description}{diff}").into()
     }
 }
 
@@ -178,13 +179,13 @@
             "
             Actual: Strukt { int: 123, string: \"something\" },
               which isn't equal to Strukt { int: 321, string: \"someone\" }
-            Difference(-actual / +expected):
-             Strukt {
-            -    int: 123,
-            +    int: 321,
-            -    string: \"something\",
-            +    string: \"someone\",
-             }
+              Difference(-actual / +expected):
+               Strukt {
+              -    int: 123,
+              +    int: 321,
+              -    string: \"something\",
+              +    string: \"someone\",
+               }
             "})))
         )
     }
@@ -200,13 +201,13 @@
             Expected: is equal to [1, 3, 4]
             Actual: [1, 2, 3],
               which isn't equal to [1, 3, 4]
-            Difference(-actual / +expected):
-             [
-                 1,
-            -    2,
-                 3,
-            +    4,
-             ]
+              Difference(-actual / +expected):
+               [
+                   1,
+              -    2,
+                   3,
+              +    4,
+               ]
             "})))
         )
     }
@@ -222,14 +223,14 @@
             Expected: is equal to [1, 3, 5]
             Actual: [1, 2, 3, 4, 5],
               which isn't equal to [1, 3, 5]
-            Difference(-actual / +expected):
-             [
-                 1,
-            -    2,
-                 3,
-            -    4,
-                 5,
-             ]
+              Difference(-actual / +expected):
+               [
+                   1,
+              -    2,
+                   3,
+              -    4,
+                   5,
+               ]
             "})))
         )
     }
@@ -241,18 +242,20 @@
             result,
             err(displays_as(contains_substring(indoc! {
             "
-            Difference(-actual / +expected):
-             [
-            -    1,
-            -    2,
-                 3,
-                 4,
-             <---- 43 common lines omitted ---->
-                 48,
-                 49,
-            +    50,
-            +    51,
-             ]"})))
+            ],
+              which isn't equal to [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
+              Difference(-actual / +expected):
+               [
+              -    1,
+              -    2,
+                   3,
+                   4,
+               <---- 43 common lines omitted ---->
+                   48,
+                   49,
+              +    50,
+              +    51,
+               ]"})))
         )
     }
 
@@ -263,18 +266,20 @@
             result,
             err(displays_as(contains_substring(indoc! {
             "
-            Difference(-actual / +expected):
-             [
-            -    1,
-            -    2,
-                 3,
-                 4,
-                 5,
-                 6,
-                 7,
-            +    8,
-            +    9,
-             ]"})))
+            Actual: [1, 2, 3, 4, 5, 6, 7],
+              which isn't equal to [3, 4, 5, 6, 7, 8, 9]
+              Difference(-actual / +expected):
+               [
+              -    1,
+              -    2,
+                   3,
+                   4,
+                   5,
+                   6,
+                   7,
+              +    8,
+              +    9,
+               ]"})))
         )
     }
 
@@ -285,15 +290,17 @@
             result,
             err(displays_as(contains_substring(indoc! {
             "
-            Difference(-actual / +expected):
-             [
-                 1,
-             <---- 46 common lines omitted ---->
-                 48,
-                 49,
-            +    50,
-            +    51,
-             ]"})))
+            ],
+              which isn't equal to [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
+              Difference(-actual / +expected):
+               [
+                   1,
+               <---- 46 common lines omitted ---->
+                   48,
+                   49,
+              +    50,
+              +    51,
+               ]"})))
         )
     }
 
@@ -304,15 +311,17 @@
             result,
             err(displays_as(contains_substring(indoc! {
             "
-            Difference(-actual / +expected):
-             [
-            -    1,
-            -    2,
-                 3,
-                 4,
-             <---- 46 common lines omitted ---->
-                 51,
-             ]"})))
+            ],
+              which isn't equal to [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
+              Difference(-actual / +expected):
+               [
+              -    1,
+              -    2,
+                   3,
+                   4,
+               <---- 46 common lines omitted ---->
+                   51,
+               ]"})))
         )
     }
 
@@ -354,14 +363,13 @@
 
         verify_that!(
             result,
-            err(displays_as(contains_substring(indoc!(
-                "
-                 First line
-                -Second line
-                +Second lines
-                 Third line
-                "
-            ))))
+            err(displays_as(contains_substring(
+                "\
+   First line
+  -Second line
+  +Second lines
+   Third line"
+            )))
         )
     }
 
diff --git a/src/matchers/err_matcher.rs b/src/matchers/err_matcher.rs
index 022bc8c..3b10de4 100644
--- a/src/matchers/err_matcher.rs
+++ b/src/matchers/err_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches a `Result` containing `Err` with a value matched by `inner`.
@@ -56,24 +59,25 @@
         actual.as_ref().err().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
     }
 
-    fn explain_match(&self, actual: &Self::ActualT) -> String {
+    fn explain_match(&self, actual: &Self::ActualT) -> Description {
         match actual {
-            Err(e) => format!("which is an error {}", self.inner.explain_match(e)),
-            Ok(_) => "which is a success".to_string(),
+            Err(e) => {
+                Description::new().text("which is an error").nested(self.inner.explain_match(e))
+            }
+            Ok(_) => "which is a success".into(),
         }
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
             MatcherResult::Match => {
-                format!("is an error which {}", self.inner.describe(MatcherResult::Match))
+                format!("is an error which {}", self.inner.describe(MatcherResult::Match)).into()
             }
-            MatcherResult::NoMatch => {
-                format!(
-                    "is a success or is an error containing a value which {}",
-                    self.inner.describe(MatcherResult::NoMatch)
-                )
-            }
+            MatcherResult::NoMatch => format!(
+                "is a success or is an error containing a value which {}",
+                self.inner.describe(MatcherResult::NoMatch)
+            )
+            .into(),
         }
     }
 }
@@ -126,7 +130,8 @@
                     Value of: Err::<i32, i32>(1)
                     Expected: is an error which is equal to 2
                     Actual: Err(1),
-                      which is an error which isn't equal to 2
+                      which is an error
+                        which isn't equal to 2
                 "
             ))))
         )
@@ -139,6 +144,9 @@
             phantom_t: Default::default(),
             phantom_e: Default::default(),
         };
-        verify_that!(matcher.describe(MatcherResult::Match), eq("is an error which is equal to 1"))
+        verify_that!(
+            matcher.describe(MatcherResult::Match),
+            displays_as(eq("is an error which is equal to 1"))
+        )
     }
 }
diff --git a/src/matchers/field_matcher.rs b/src/matchers/field_matcher.rs
index 0d1d4fe..fc37ce6 100644
--- a/src/matchers/field_matcher.rs
+++ b/src/matchers/field_matcher.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // There are no visible documentation elements in this module; the declarative
-// macro is documented at the top level.
+// macro is documented in the matchers module.
 #![doc(hidden)]
 
 /// Matches a structure or enum with a given field which is matched by a given
@@ -105,10 +105,11 @@
 /// # }
 /// ```
 ///
-/// See also the macro [`property`][crate::property] for an analogous mechanism
-/// to extract a datum by invoking a method.
+/// See also the macro [`property`][crate::matchers::property] for an analogous
+/// mechanism to extract a datum by invoking a method.
 #[macro_export]
-macro_rules! field {
+#[doc(hidden)]
+macro_rules! __field {
     ($($t:tt)*) => { $crate::field_internal!($($t)*) }
 }
 
@@ -118,7 +119,7 @@
 #[macro_export]
 macro_rules! field_internal {
     ($($t:ident)::+.$field:tt, $m:expr) => {{
-        use $crate::matchers::field_matcher::internal::field_matcher;
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher;
         field_matcher(
             |o| {
                 match o {
@@ -140,7 +141,10 @@
 /// **For internal use only. API stablility is not guaranteed!**
 #[doc(hidden)]
 pub mod internal {
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::{
+        description::Description,
+        matcher::{Matcher, MatcherResult},
+    };
     use std::fmt::Debug;
 
     /// Creates a matcher to verify a specific field of the actual struct using
@@ -175,27 +179,29 @@
             }
         }
 
-        fn explain_match(&self, actual: &OuterT) -> String {
+        fn explain_match(&self, actual: &OuterT) -> Description {
             if let Some(actual) = (self.field_accessor)(actual) {
                 format!(
                     "which has field `{}`, {}",
                     self.field_path,
                     self.inner.explain_match(actual)
                 )
+                .into()
             } else {
                 let formatted_actual_value = format!("{actual:?}");
                 let without_fields = formatted_actual_value.split('(').next().unwrap_or("");
                 let without_fields = without_fields.split('{').next().unwrap_or("").trim_end();
-                format!("which has the wrong enum variant `{without_fields}`")
+                format!("which has the wrong enum variant `{without_fields}`").into()
             }
         }
 
-        fn describe(&self, matcher_result: MatcherResult) -> String {
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
             format!(
                 "has field `{}`, which {}",
                 self.field_path,
                 self.inner.describe(matcher_result)
             )
+            .into()
         }
     }
 }
diff --git a/src/matchers/ge_matcher.rs b/src/matchers/ge_matcher.rs
index 33e847e..cb2a91f 100644
--- a/src/matchers/ge_matcher.rs
+++ b/src/matchers/ge_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches a value greater than or equal to (in the sense of `>=`) `expected`.
@@ -91,10 +94,12 @@
         (*actual >= self.expected).into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
-            MatcherResult::Match => format!("is greater than or equal to {:?}", self.expected),
-            MatcherResult::NoMatch => format!("is less than {:?}", self.expected),
+            MatcherResult::Match => {
+                format!("is greater than or equal to {:?}", self.expected).into()
+            }
+            MatcherResult::NoMatch => format!("is less than {:?}", self.expected).into(),
         }
     }
 }
diff --git a/src/matchers/gt_matcher.rs b/src/matchers/gt_matcher.rs
index 699bf2a..b52c6e3 100644
--- a/src/matchers/gt_matcher.rs
+++ b/src/matchers/gt_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches a value greater (in the sense of `>`) than `expected`.
@@ -91,10 +94,12 @@
         (*actual > self.expected).into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
-            MatcherResult::Match => format!("is greater than {:?}", self.expected),
-            MatcherResult::NoMatch => format!("is less than or equal to {:?}", self.expected),
+            MatcherResult::Match => format!("is greater than {:?}", self.expected).into(),
+            MatcherResult::NoMatch => {
+                format!("is less than or equal to {:?}", self.expected).into()
+            }
         }
     }
 }
@@ -175,14 +180,17 @@
 
     #[test]
     fn gt_describe_matches() -> Result<()> {
-        verify_that!(gt::<i32, i32>(232).describe(MatcherResult::Match), eq("is greater than 232"))
+        verify_that!(
+            gt::<i32, i32>(232).describe(MatcherResult::Match),
+            displays_as(eq("is greater than 232"))
+        )
     }
 
     #[test]
     fn gt_describe_does_not_match() -> Result<()> {
         verify_that!(
             gt::<i32, i32>(232).describe(MatcherResult::NoMatch),
-            eq("is less than or equal to 232")
+            displays_as(eq("is less than or equal to 232"))
         )
     }
 
diff --git a/src/matchers/has_entry_matcher.rs b/src/matchers/has_entry_matcher.rs
index 67a44cd..7f9d2ad 100644
--- a/src/matchers/has_entry_matcher.rs
+++ b/src/matchers/has_entry_matcher.rs
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use crate::description::Description;
 use crate::matcher::{Matcher, MatcherResult};
 use std::collections::HashMap;
 use std::fmt::Debug;
@@ -87,7 +88,7 @@
         }
     }
 
-    fn explain_match(&self, actual: &HashMap<KeyT, ValueT>) -> String {
+    fn explain_match(&self, actual: &HashMap<KeyT, ValueT>) -> Description {
         if let Some(value) = actual.get(&self.key) {
             format!(
                 "which contains key {:?}, but is mapped to value {:#?}, {}",
@@ -95,24 +96,27 @@
                 value,
                 self.inner.explain_match(value)
             )
+            .into()
         } else {
-            format!("which doesn't contain key {:?}", self.key)
+            format!("which doesn't contain key {:?}", self.key).into()
         }
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
             MatcherResult::Match => format!(
                 "contains key {:?}, which value {}",
                 self.key,
                 self.inner.describe(MatcherResult::Match)
-            ),
+            )
+            .into(),
             MatcherResult::NoMatch => format!(
                 "doesn't contain key {:?} or contains key {:?}, which value {}",
                 self.key,
                 self.key,
                 self.inner.describe(MatcherResult::NoMatch)
-            ),
+            )
+            .into(),
         }
     }
 }
diff --git a/src/matchers/is_encoded_string_matcher.rs b/src/matchers/is_encoded_string_matcher.rs
new file mode 100644
index 0000000..d2fb259
--- /dev/null
+++ b/src/matchers/is_encoded_string_matcher.rs
@@ -0,0 +1,180 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
+use std::{fmt::Debug, marker::PhantomData};
+
+/// Matches a byte sequence which is a UTF-8 encoded string matched by `inner`.
+///
+/// The matcher reports no match if either the string is not UTF-8 encoded or if
+/// `inner` does not match on the decoded string.
+///
+/// The input may be a slice `&[u8]` or a `Vec` of bytes.
+///
+/// ```
+/// # use googletest::prelude::*;
+/// # fn should_pass() -> Result<()> {
+/// let bytes: &[u8] = "A string".as_bytes();
+/// verify_that!(bytes, is_utf8_string(eq("A string")))?; // Passes
+/// let bytes: Vec<u8> = "A string".as_bytes().to_vec();
+/// verify_that!(bytes, is_utf8_string(eq("A string")))?; // Passes
+/// #     Ok(())
+/// # }
+/// # fn should_fail_1() -> Result<()> {
+/// # let bytes: &[u8] = "A string".as_bytes();
+/// verify_that!(bytes, is_utf8_string(eq("Another string")))?; // Fails (inner matcher does not match)
+/// #     Ok(())
+/// # }
+/// # fn should_fail_2() -> Result<()> {
+/// let bytes: Vec<u8> = vec![255, 64, 128, 32];
+/// verify_that!(bytes, is_utf8_string(anything()))?; // Fails (not UTF-8 encoded)
+/// #     Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// # should_fail_1().unwrap_err();
+/// # should_fail_2().unwrap_err();
+/// ```
+pub fn is_utf8_string<'a, ActualT: AsRef<[u8]> + Debug + 'a, InnerMatcherT>(
+    inner: InnerMatcherT,
+) -> impl Matcher<ActualT = ActualT>
+where
+    InnerMatcherT: Matcher<ActualT = String>,
+{
+    IsEncodedStringMatcher { inner, phantom: Default::default() }
+}
+
+struct IsEncodedStringMatcher<ActualT, InnerMatcherT> {
+    inner: InnerMatcherT,
+    phantom: PhantomData<ActualT>,
+}
+
+impl<'a, ActualT: AsRef<[u8]> + Debug + 'a, InnerMatcherT> Matcher
+    for IsEncodedStringMatcher<ActualT, InnerMatcherT>
+where
+    InnerMatcherT: Matcher<ActualT = String>,
+{
+    type ActualT = ActualT;
+
+    fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+        String::from_utf8(actual.as_ref().to_vec())
+            .map(|s| self.inner.matches(&s))
+            .unwrap_or(MatcherResult::NoMatch)
+    }
+
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
+        match matcher_result {
+            MatcherResult::Match => format!(
+                "is a UTF-8 encoded string which {}",
+                self.inner.describe(MatcherResult::Match)
+            )
+            .into(),
+            MatcherResult::NoMatch => format!(
+                "is not a UTF-8 encoded string which {}",
+                self.inner.describe(MatcherResult::Match)
+            )
+            .into(),
+        }
+    }
+
+    fn explain_match(&self, actual: &Self::ActualT) -> Description {
+        match String::from_utf8(actual.as_ref().to_vec()) {
+            Ok(s) => {
+                format!("which is a UTF-8 encoded string {}", self.inner.explain_match(&s)).into()
+            }
+            Err(e) => format!("which is not a UTF-8 encoded string: {e}").into(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::matcher::MatcherResult;
+    use crate::prelude::*;
+
+    #[test]
+    fn matches_string_as_byte_slice() -> Result<()> {
+        verify_that!("A string".as_bytes(), is_utf8_string(eq("A string")))
+    }
+
+    #[test]
+    fn matches_string_as_byte_vec() -> Result<()> {
+        verify_that!("A string".as_bytes().to_vec(), is_utf8_string(eq("A string")))
+    }
+
+    #[test]
+    fn matches_string_with_utf_8_encoded_sequences() -> Result<()> {
+        verify_that!("äöüÄÖÜ".as_bytes().to_vec(), is_utf8_string(eq("äöüÄÖÜ")))
+    }
+
+    #[test]
+    fn does_not_match_non_equal_string() -> Result<()> {
+        verify_that!("äöüÄÖÜ".as_bytes().to_vec(), not(is_utf8_string(eq("A string"))))
+    }
+
+    #[test]
+    fn does_not_match_non_utf_8_encoded_byte_sequence() -> Result<()> {
+        verify_that!(&[192, 64, 255, 32], not(is_utf8_string(eq("A string"))))
+    }
+
+    #[test]
+    fn has_correct_description_in_matched_case() -> Result<()> {
+        let matcher = is_utf8_string::<&[u8], _>(eq("A string"));
+
+        verify_that!(
+            matcher.describe(MatcherResult::Match),
+            displays_as(eq("is a UTF-8 encoded string which is equal to \"A string\""))
+        )
+    }
+
+    #[test]
+    fn has_correct_description_in_not_matched_case() -> Result<()> {
+        let matcher = is_utf8_string::<&[u8], _>(eq("A string"));
+
+        verify_that!(
+            matcher.describe(MatcherResult::NoMatch),
+            displays_as(eq("is not a UTF-8 encoded string which is equal to \"A string\""))
+        )
+    }
+
+    #[test]
+    fn has_correct_explanation_in_matched_case() -> Result<()> {
+        let explanation = is_utf8_string(eq("A string")).explain_match(&"A string".as_bytes());
+
+        verify_that!(
+            explanation,
+            displays_as(eq("which is a UTF-8 encoded string which is equal to \"A string\""))
+        )
+    }
+
+    #[test]
+    fn has_correct_explanation_when_byte_array_is_not_utf8_encoded() -> Result<()> {
+        let explanation = is_utf8_string(eq("A string")).explain_match(&&[192, 128, 0, 64]);
+
+        verify_that!(explanation, displays_as(starts_with("which is not a UTF-8 encoded string: ")))
+    }
+
+    #[test]
+    fn has_correct_explanation_when_inner_matcher_does_not_match() -> Result<()> {
+        let explanation =
+            is_utf8_string(eq("A string")).explain_match(&"Another string".as_bytes());
+
+        verify_that!(
+            explanation,
+            displays_as(eq("which is a UTF-8 encoded string which isn't equal to \"A string\""))
+        )
+    }
+}
diff --git a/src/matchers/is_matcher.rs b/src/matchers/is_matcher.rs
index 51fd3b2..336ce53 100644
--- a/src/matchers/is_matcher.rs
+++ b/src/matchers/is_matcher.rs
@@ -14,7 +14,10 @@
 
 #![doc(hidden)]
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches precisely values matched by `inner`.
@@ -44,22 +47,24 @@
         self.inner.matches(actual)
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
             MatcherResult::Match => format!(
                 "is {} which {}",
                 self.description,
                 self.inner.describe(MatcherResult::Match)
-            ),
+            )
+            .into(),
             MatcherResult::NoMatch => format!(
                 "is not {} which {}",
                 self.description,
                 self.inner.describe(MatcherResult::Match)
-            ),
+            )
+            .into(),
         }
     }
 
-    fn explain_match(&self, actual: &Self::ActualT) -> String {
+    fn explain_match(&self, actual: &Self::ActualT) -> Description {
         self.inner.explain_match(actual)
     }
 }
diff --git a/src/matchers/is_nan_matcher.rs b/src/matchers/is_nan_matcher.rs
index 3cbe694..0af4338 100644
--- a/src/matchers/is_nan_matcher.rs
+++ b/src/matchers/is_nan_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use num_traits::float::Float;
 use std::{fmt::Debug, marker::PhantomData};
 
@@ -30,8 +33,8 @@
         actual.is_nan().into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
-        if matcher_result.into() { "is NaN" } else { "isn't NaN" }.to_string()
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
+        if matcher_result.into() { "is NaN" } else { "isn't NaN" }.into()
     }
 }
 
diff --git a/src/matchers/le_matcher.rs b/src/matchers/le_matcher.rs
index 6e7a16f..cfc5781 100644
--- a/src/matchers/le_matcher.rs
+++ b/src/matchers/le_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches a value less than or equal to (in the sense of `<=`) `expected`.
@@ -91,10 +94,10 @@
         (*actual <= self.expected).into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
-            MatcherResult::Match => format!("is less than or equal to {:?}", self.expected),
-            MatcherResult::NoMatch => format!("is greater than {:?}", self.expected),
+            MatcherResult::Match => format!("is less than or equal to {:?}", self.expected).into(),
+            MatcherResult::NoMatch => format!("is greater than {:?}", self.expected).into(),
         }
     }
 }
diff --git a/src/matchers/len_matcher.rs b/src/matchers/len_matcher.rs
index 3a31873..be903c9 100644
--- a/src/matchers/len_matcher.rs
+++ b/src/matchers/len_matcher.rs
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use crate::description::Description;
 use crate::matcher::{Matcher, MatcherResult};
 use crate::matcher_support::count_elements::count_elements;
 use std::{fmt::Debug, marker::PhantomData};
@@ -19,7 +20,9 @@
 /// Matches a container whose number of elements matches `expected`.
 ///
 /// This matches against a container over which one can iterate. This includes
-/// the standard Rust containers, arrays, and (when dereferenced) slices.
+/// the standard Rust containers, arrays, and (when dereferenced) slices. More
+/// precisely, a shared borrow of the actual type must implement
+/// [`IntoIterator`].
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -68,26 +71,29 @@
         self.expected.matches(&count_elements(actual))
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
             MatcherResult::Match => {
-                format!("has length, which {}", self.expected.describe(MatcherResult::Match))
+                format!("has length, which {}", self.expected.describe(MatcherResult::Match)).into()
             }
             MatcherResult::NoMatch => {
                 format!("has length, which {}", self.expected.describe(MatcherResult::NoMatch))
+                    .into()
             }
         }
     }
 
-    fn explain_match(&self, actual: &T) -> String {
+    fn explain_match(&self, actual: &T) -> Description {
         let actual_size = count_elements(actual);
         format!("which has length {}, {}", actual_size, self.expected.explain_match(&actual_size))
+            .into()
     }
 }
 
 #[cfg(test)]
 mod tests {
     use super::len;
+    use crate::description::Description;
     use crate::matcher::{Matcher, MatcherResult};
     use crate::prelude::*;
     use indoc::indoc;
@@ -180,11 +186,11 @@
                 false.into()
             }
 
-            fn describe(&self, _: MatcherResult) -> String {
+            fn describe(&self, _: MatcherResult) -> Description {
                 "called described".into()
             }
 
-            fn explain_match(&self, _: &T) -> String {
+            fn explain_match(&self, _: &T) -> Description {
                 "called explain_match".into()
             }
         }
diff --git a/src/matchers/lt_matcher.rs b/src/matchers/lt_matcher.rs
index 96df00c..08bc563 100644
--- a/src/matchers/lt_matcher.rs
+++ b/src/matchers/lt_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches a value less (in the sense of `<`) than `expected`.
@@ -91,11 +94,11 @@
         (*actual < self.expected).into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
-            MatcherResult::Match => format!("is less than {:?}", self.expected),
+            MatcherResult::Match => format!("is less than {:?}", self.expected).into(),
             MatcherResult::NoMatch => {
-                format!("is greater than or equal to {:?}", self.expected)
+                format!("is greater than or equal to {:?}", self.expected).into()
             }
         }
     }
diff --git a/src/matchers/matches_pattern.rs b/src/matchers/matches_pattern.rs
index 9c252e5..106a5d7 100644
--- a/src/matchers/matches_pattern.rs
+++ b/src/matchers/matches_pattern.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // There are no visible documentation elements in this module; the declarative
-// macro is documented at the top level.
+// macro is documented in the matchers module.
 #![doc(hidden)]
 
 /// Matches a value according to a pattern of matchers.
@@ -91,7 +91,8 @@
 /// #     .unwrap();
 /// ```
 ///
-/// One can use the alias [`pat`][crate::pat] to make this less verbose:
+/// One can use the alias [`pat`][crate::matchers::pat] to make this less
+/// verbose:
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -162,7 +163,7 @@
 /// #     .unwrap();
 /// ```
 ///
-/// If the method returns a reference, precede it with the keyword `ref`:
+/// If the method returns a reference, precede it with a `*`:
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -177,7 +178,7 @@
 ///
 /// # let my_struct = MyStruct { a_field: "Something to believe in".into() };
 /// verify_that!(my_struct, matches_pattern!(MyStruct {
-///     ref get_a_field_ref(): starts_with("Something"),
+///     *get_a_field_ref(): starts_with("Something"),
 /// }))
 /// #    .unwrap();
 /// ```
@@ -246,7 +247,8 @@
 /// #    .unwrap();
 /// ```
 #[macro_export]
-macro_rules! matches_pattern {
+#[doc(hidden)]
+macro_rules! __matches_pattern {
     ($($t:tt)*) => { $crate::matches_pattern_internal!($($t)*) }
 }
 
@@ -259,7 +261,7 @@
         [$($struct_name:tt)*],
         { $field_name:ident : $matcher:expr $(,)? }
     ) => {
-        $crate::matchers::is_matcher::is(
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
             stringify!($($struct_name)*),
             all!(field!($($struct_name)*.$field_name, $matcher))
         )
@@ -269,7 +271,7 @@
         [$($struct_name:tt)*],
         { $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
     ) => {
-        $crate::matchers::is_matcher::is(
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
             stringify!($($struct_name)*),
             all!(property!($($struct_name)*.$property_name($($argument),*), $matcher))
         )
@@ -277,11 +279,11 @@
 
     (
         [$($struct_name:tt)*],
-        { ref $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
+        { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
     ) => {
-        $crate::matchers::is_matcher::is(
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
             stringify!($($struct_name)*),
-            all!(property!(ref $($struct_name)*.$property_name($($argument),*), $matcher))
+            all!(property!(* $($struct_name)*.$property_name($($argument),*), $matcher))
         )
     };
 
@@ -309,10 +311,10 @@
 
     (
         [$($struct_name:tt)*],
-        { ref $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* }
+        { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* }
     ) => {
         $crate::matches_pattern_internal!(
-            all!(property!(ref $($struct_name)*.$property_name($($argument),*), $matcher)),
+            all!(property!(* $($struct_name)*.$property_name($($argument),*), $matcher)),
             [$($struct_name)*],
             { $($rest)* }
         )
@@ -323,7 +325,7 @@
         [$($struct_name:tt)*],
         { $field_name:ident : $matcher:expr $(,)? }
     ) => {
-        $crate::matchers::is_matcher::is(stringify!($($struct_name)*), all!(
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!(
             $($processed)*,
             field!($($struct_name)*.$field_name, $matcher)
         ))
@@ -334,7 +336,7 @@
         [$($struct_name:tt)*],
         { $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
     ) => {
-        $crate::matchers::is_matcher::is(stringify!($($struct_name)*), all!(
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!(
             $($processed)*,
             property!($($struct_name)*.$property_name($($argument),*), $matcher)
         ))
@@ -343,11 +345,11 @@
     (
         all!($($processed:tt)*),
         [$($struct_name:tt)*],
-        { ref $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
+        { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
     ) => {
-        $crate::matchers::is_matcher::is(stringify!($($struct_name)*), all!(
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!(
             $($processed)*,
-            property!(ref $($struct_name)*.$property_name($($argument),*), $matcher)
+            property!(* $($struct_name)*.$property_name($($argument),*), $matcher)
         ))
     };
 
@@ -384,12 +386,12 @@
     (
         all!($($processed:tt)*),
         [$($struct_name:tt)*],
-        { ref $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* }
+        { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* }
     ) => {
         $crate::matches_pattern_internal!(
             all!(
                 $($processed)*,
-                property!(ref $($struct_name)*.$property_name($($argument),*), $matcher)
+                property!(* $($struct_name)*.$property_name($($argument),*), $matcher)
             ),
             [$($struct_name)*],
             { $($rest)* }
@@ -410,7 +412,7 @@
         [$($struct_name:tt)*],
         ($matcher:expr $(,)?)
     ) => {
-        $crate::matchers::is_matcher::is(
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
             stringify!($($struct_name)*),
             all!(field!($($struct_name)*.0, $matcher))
         )
@@ -436,7 +438,7 @@
         $field:tt,
         ($matcher:expr $(,)?)
     ) => {
-        $crate::matchers::is_matcher::is(stringify!($($struct_name)*), all!(
+        $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!(
             $($processed)*,
             field!($($struct_name)*.$field, $matcher)
         ))
@@ -587,13 +589,14 @@
 
     ($first:tt $($rest:tt)*) => {{
         #[allow(unused)]
-        use $crate::{all, field, property};
+        use $crate::matchers::{all, field, property};
         $crate::matches_pattern_internal!([$first], $($rest)*)
     }};
 }
 
-/// An alias for [`matches_pattern`].
+/// An alias for [`matches_pattern`][crate::matchers::matches_pattern!].
 #[macro_export]
-macro_rules! pat {
+#[doc(hidden)]
+macro_rules! __pat {
     ($($t:tt)*) => { $crate::matches_pattern_internal!($($t)*) }
 }
diff --git a/src/matchers/matches_regex_matcher.rs b/src/matchers/matches_regex_matcher.rs
index d0001e2..32b053b 100644
--- a/src/matchers/matches_regex_matcher.rs
+++ b/src/matchers/matches_regex_matcher.rs
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use crate::description::Description;
 use crate::matcher::{Matcher, MatcherResult};
 use regex::Regex;
 use std::fmt::Debug;
@@ -94,13 +95,13 @@
         self.regex.is_match(actual.as_ref()).into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
             MatcherResult::Match => {
-                format!("matches the regular expression {:#?}", self.pattern.deref())
+                format!("matches the regular expression {:#?}", self.pattern.deref()).into()
             }
             MatcherResult::NoMatch => {
-                format!("doesn't match the regular expression {:#?}", self.pattern.deref())
+                format!("doesn't match the regular expression {:#?}", self.pattern.deref()).into()
             }
         }
     }
@@ -204,7 +205,7 @@
 
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq("matches the regular expression \"\\n\"")
+            displays_as(eq("matches the regular expression \"\\n\""))
         )
     }
 }
diff --git a/src/matchers/mod.rs b/src/matchers/mod.rs
index f8aef10..1e028b9 100644
--- a/src/matchers/mod.rs
+++ b/src/matchers/mod.rs
@@ -14,27 +14,28 @@
 
 //! All built-in matchers of this crate are in submodules of this module.
 
-pub mod all_matcher;
-pub mod any_matcher;
+mod all_matcher;
+mod any_matcher;
 mod anything_matcher;
 mod char_count_matcher;
-pub mod conjunction_matcher;
+mod conjunction_matcher;
 mod container_eq_matcher;
 mod contains_matcher;
 mod contains_regex_matcher;
-pub mod disjunction_matcher;
+mod disjunction_matcher;
 mod display_matcher;
 mod each_matcher;
-pub mod elements_are_matcher;
+mod elements_are_matcher;
 mod empty_matcher;
 mod eq_deref_of_matcher;
 mod eq_matcher;
 mod err_matcher;
-pub mod field_matcher;
+mod field_matcher;
 mod ge_matcher;
 mod gt_matcher;
 mod has_entry_matcher;
-pub mod is_matcher;
+mod is_encoded_string_matcher;
+mod is_matcher;
 mod is_nan_matcher;
 mod le_matcher;
 mod len_matcher;
@@ -46,15 +47,15 @@
 mod not_matcher;
 mod ok_matcher;
 mod points_to_matcher;
-pub mod pointwise_matcher;
+mod pointwise_matcher;
 mod predicate_matcher;
-pub mod property_matcher;
+mod property_matcher;
 mod some_matcher;
 mod str_matcher;
 mod subset_of_matcher;
 mod superset_of_matcher;
 mod tuple_matcher;
-pub mod unordered_elements_are_matcher;
+mod unordered_elements_are_matcher;
 
 pub use anything_matcher::anything;
 pub use char_count_matcher::char_count;
@@ -70,6 +71,7 @@
 pub use ge_matcher::ge;
 pub use gt_matcher::gt;
 pub use has_entry_matcher::has_entry;
+pub use is_encoded_string_matcher::is_utf8_string;
 pub use is_nan_matcher::is_nan;
 pub use le_matcher::le;
 pub use len_matcher::len;
@@ -87,3 +89,32 @@
 };
 pub use subset_of_matcher::subset_of;
 pub use superset_of_matcher::superset_of;
+
+// Reexport and unmangle the macros.
+#[doc(inline)]
+pub use crate::{
+    __all as all, __any as any, __contains_each as contains_each, __elements_are as elements_are,
+    __field as field, __is_contained_in as is_contained_in, __matches_pattern as matches_pattern,
+    __pat as pat, __pointwise as pointwise, __property as property,
+    __unordered_elements_are as unordered_elements_are,
+};
+
+// Types and functions used by macros matchers.
+// Do not use directly.
+// We may perform incompatible changes without major release. These elements
+// should only be used through their respective macros.
+#[doc(hidden)]
+pub mod __internal_unstable_do_not_depend_on_these {
+    pub use super::all_matcher::internal::AllMatcher;
+    pub use super::any_matcher::internal::AnyMatcher;
+    pub use super::conjunction_matcher::ConjunctionMatcher;
+    pub use super::disjunction_matcher::DisjunctionMatcher;
+    pub use super::elements_are_matcher::internal::ElementsAre;
+    pub use super::field_matcher::internal::field_matcher;
+    pub use super::is_matcher::is;
+    pub use super::pointwise_matcher::internal::PointwiseMatcher;
+    pub use super::property_matcher::internal::{property_matcher, property_ref_matcher};
+    pub use super::unordered_elements_are_matcher::internal::{
+        Requirements, UnorderedElementsAreMatcher, UnorderedElementsOfMapAreMatcher,
+    };
+}
diff --git a/src/matchers/near_matcher.rs b/src/matchers/near_matcher.rs
index 484939c..ca7cbdf 100644
--- a/src/matchers/near_matcher.rs
+++ b/src/matchers/near_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use num_traits::{Float, FloatConst};
 use std::fmt::Debug;
 
@@ -179,13 +182,13 @@
         }
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
             MatcherResult::Match => {
-                format!("is within {:?} of {:?}", self.max_abs_error, self.expected)
+                format!("is within {:?} of {:?}", self.max_abs_error, self.expected).into()
             }
             MatcherResult::NoMatch => {
-                format!("isn't within {:?} of {:?}", self.max_abs_error, self.expected)
+                format!("isn't within {:?} of {:?}", self.max_abs_error, self.expected).into()
             }
         }
     }
diff --git a/src/matchers/none_matcher.rs b/src/matchers/none_matcher.rs
index e48d549..af28932 100644
--- a/src/matchers/none_matcher.rs
+++ b/src/matchers/none_matcher.rs
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use crate::description::Description;
 use crate::matcher::{Matcher, MatcherResult};
 use std::fmt::Debug;
 use std::marker::PhantomData;
@@ -46,10 +47,10 @@
         (actual.is_none()).into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
-            MatcherResult::Match => "is none".to_string(),
-            MatcherResult::NoMatch => "is some(_)".to_string(),
+            MatcherResult::Match => "is none".into(),
+            MatcherResult::NoMatch => "is some(_)".into(),
         }
     }
 }
diff --git a/src/matchers/not_matcher.rs b/src/matchers/not_matcher.rs
index 1dff791..f03d4ce 100644
--- a/src/matchers/not_matcher.rs
+++ b/src/matchers/not_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches the actual value exactly when the inner matcher does _not_ match.
@@ -51,11 +54,11 @@
         }
     }
 
-    fn explain_match(&self, actual: &T) -> String {
+    fn explain_match(&self, actual: &T) -> Description {
         self.inner.explain_match(actual)
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         self.inner.describe(if matcher_result.into() {
             MatcherResult::NoMatch
         } else {
diff --git a/src/matchers/ok_matcher.rs b/src/matchers/ok_matcher.rs
index 5c6fa51..8425b93 100644
--- a/src/matchers/ok_matcher.rs
+++ b/src/matchers/ok_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches a `Result` containing `Ok` with a value matched by `inner`.
@@ -56,27 +59,27 @@
         actual.as_ref().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
     }
 
-    fn explain_match(&self, actual: &Self::ActualT) -> String {
+    fn explain_match(&self, actual: &Self::ActualT) -> Description {
         match actual {
-            Ok(o) => format!("which is a success {}", self.inner.explain_match(o)),
-            Err(_) => "which is an error".to_string(),
+            Ok(o) => {
+                Description::new().text("which is a success").nested(self.inner.explain_match(o))
+            }
+            Err(_) => "which is an error".into(),
         }
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
-            MatcherResult::Match => {
-                format!(
-                    "is a success containing a value, which {}",
-                    self.inner.describe(MatcherResult::Match)
-                )
-            }
-            MatcherResult::NoMatch => {
-                format!(
-                    "is an error or a success containing a value, which {}",
-                    self.inner.describe(MatcherResult::NoMatch)
-                )
-            }
+            MatcherResult::Match => format!(
+                "is a success containing a value, which {}",
+                self.inner.describe(MatcherResult::Match)
+            )
+            .into(),
+            MatcherResult::NoMatch => format!(
+                "is an error or a success containing a value, which {}",
+                self.inner.describe(MatcherResult::NoMatch)
+            )
+            .into(),
         }
     }
 }
@@ -129,7 +132,8 @@
                     Value of: Ok::<i32, i32>(1)
                     Expected: is a success containing a value, which is equal to 2
                     Actual: Ok(1),
-                      which is a success which isn't equal to 2
+                      which is a success
+                        which isn't equal to 2
                 "
             ))))
         )
@@ -144,7 +148,7 @@
         };
         verify_that!(
             matcher.describe(MatcherResult::Match),
-            eq("is a success containing a value, which is equal to 1")
+            displays_as(eq("is a success containing a value, which is equal to 1"))
         )
     }
 }
diff --git a/src/matchers/points_to_matcher.rs b/src/matchers/points_to_matcher.rs
index 08c7343..2c516d0 100644
--- a/src/matchers/points_to_matcher.rs
+++ b/src/matchers/points_to_matcher.rs
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+use crate::description::Description;
 use crate::matcher::{Matcher, MatcherResult};
 use std::fmt::Debug;
 use std::marker::PhantomData;
@@ -59,11 +60,11 @@
         self.expected.matches(actual.deref())
     }
 
-    fn explain_match(&self, actual: &ActualT) -> String {
+    fn explain_match(&self, actual: &ActualT) -> Description {
         self.expected.explain_match(actual.deref())
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         self.expected.describe(matcher_result)
     }
 }
diff --git a/src/matchers/pointwise_matcher.rs b/src/matchers/pointwise_matcher.rs
index 5ee0f22..01e70c0 100644
--- a/src/matchers/pointwise_matcher.rs
+++ b/src/matchers/pointwise_matcher.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // There are no visible documentation elements in this module; the declarative
-// macro is documented at the top level.
+// macro is documented in the matcher module.
 #![doc(hidden)]
 
 /// Generates a matcher which matches a container each of whose elements match
@@ -79,8 +79,9 @@
 /// that all of the containers have the same size. This matcher does not check
 /// whether the sizes match.
 ///
-/// The actual value must be a container implementing [`IntoIterator`]. This
-/// includes standard containers, slices (when dereferenced) and arrays.
+/// The actual value must be a container such as a `Vec`, an array, or a
+/// dereferenced slice. More precisely, a shared borrow of the actual value must
+/// implement [`IntoIterator`].
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -115,14 +116,15 @@
 /// [`Pointwise`]: https://google.github.io/googletest/reference/matchers.html#container-matchers
 /// [`Vec`]: std::vec::Vec
 #[macro_export]
-macro_rules! pointwise {
+#[doc(hidden)]
+macro_rules! __pointwise {
     ($matcher:expr, $container:expr) => {{
-        use $crate::matchers::pointwise_matcher::internal::PointwiseMatcher;
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher;
         PointwiseMatcher::new($container.into_iter().map($matcher).collect())
     }};
 
     ($matcher:expr, $left_container:expr, $right_container:expr) => {{
-        use $crate::matchers::pointwise_matcher::internal::PointwiseMatcher;
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher;
         PointwiseMatcher::new(
             $left_container
                 .into_iter()
@@ -133,7 +135,7 @@
     }};
 
     ($matcher:expr, $left_container:expr, $middle_container:expr, $right_container:expr) => {{
-        use $crate::matchers::pointwise_matcher::internal::PointwiseMatcher;
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher;
         PointwiseMatcher::new(
             $left_container
                 .into_iter()
@@ -149,8 +151,8 @@
 /// **For internal use only. API stablility is not guaranteed!**
 #[doc(hidden)]
 pub mod internal {
+    use crate::description::Description;
     use crate::matcher::{Matcher, MatcherResult};
-    use crate::matcher_support::description::Description;
     use crate::matcher_support::zipped_iterator::zip;
     use std::{fmt::Debug, marker::PhantomData};
 
@@ -190,7 +192,7 @@
             }
         }
 
-        fn explain_match(&self, actual: &ContainerT) -> String {
+        fn explain_match(&self, actual: &ContainerT) -> Description {
             // TODO(b/260819741) This code duplicates elements_are_matcher.rs. Consider
             // extract as a separate library. (or implement pointwise! with
             // elements_are)
@@ -204,23 +206,24 @@
             }
             if mismatches.is_empty() {
                 if !zipped_iterator.has_size_mismatch() {
-                    "which matches all elements".to_string()
+                    "which matches all elements".into()
                 } else {
                     format!(
                         "which has size {} (expected {})",
                         zipped_iterator.left_size(),
                         self.matchers.len()
                     )
+                    .into()
                 }
             } else if mismatches.len() == 1 {
-                format!("where {}", mismatches[0])
+                format!("where {}", mismatches[0]).into()
             } else {
                 let mismatches = mismatches.into_iter().collect::<Description>();
-                format!("where:\n{}", mismatches.bullet_list().indent())
+                format!("where:\n{}", mismatches.bullet_list().indent()).into()
             }
         }
 
-        fn describe(&self, matcher_result: MatcherResult) -> String {
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
             format!(
                 "{} elements satisfying respectively:\n{}",
                 if matcher_result.into() { "has" } else { "doesn't have" },
@@ -231,6 +234,7 @@
                     .enumerate()
                     .indent()
             )
+            .into()
         }
     }
 }
diff --git a/src/matchers/predicate_matcher.rs b/src/matchers/predicate_matcher.rs
index fabd6c3..5bc067d 100644
--- a/src/matchers/predicate_matcher.rs
+++ b/src/matchers/predicate_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Creates a matcher based on the predicate provided.
@@ -92,18 +95,18 @@
 ///
 /// See [`PredicateMatcher::with_description`]
 pub trait PredicateDescription {
-    fn to_description(&self) -> String;
+    fn to_description(&self) -> Description;
 }
 
 impl PredicateDescription for &str {
-    fn to_description(&self) -> String {
-        self.to_string()
+    fn to_description(&self) -> Description {
+        self.to_string().into()
     }
 }
 
 impl PredicateDescription for String {
-    fn to_description(&self) -> String {
-        self.clone()
+    fn to_description(&self) -> Description {
+        self.to_string().into()
     }
 }
 
@@ -112,8 +115,8 @@
     T: Fn() -> S,
     S: Into<String>,
 {
-    fn to_description(&self) -> String {
-        self().into()
+    fn to_description(&self) -> Description {
+        self().into().into()
     }
 }
 
@@ -131,10 +134,10 @@
         (self.predicate)(actual).into()
     }
 
-    fn describe(&self, result: MatcherResult) -> String {
+    fn describe(&self, result: MatcherResult) -> Description {
         match result {
-            MatcherResult::Match => "matches".to_string(),
-            MatcherResult::NoMatch => "does not match".to_string(),
+            MatcherResult::Match => "matches".into(),
+            MatcherResult::NoMatch => "does not match".into(),
         }
     }
 }
@@ -150,7 +153,7 @@
         (self.predicate)(actual).into()
     }
 
-    fn describe(&self, result: MatcherResult) -> String {
+    fn describe(&self, result: MatcherResult) -> Description {
         match result {
             MatcherResult::Match => self.positive_description.to_description(),
             MatcherResult::NoMatch => self.negative_description.to_description(),
diff --git a/src/matchers/property_matcher.rs b/src/matchers/property_matcher.rs
index d69ba1d..19b4862 100644
--- a/src/matchers/property_matcher.rs
+++ b/src/matchers/property_matcher.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // There are no visible documentation elements in this module; the declarative
-// macro is documented at the top level.
+// macro is documented in the matcher module.
 #![doc(hidden)]
 
 /// Matches an object which, upon calling the given method on it with the given
@@ -44,8 +44,7 @@
 /// failure, it will be invoked a second time, with the assertion failure output
 /// reflecting the *second* invocation.
 ///
-/// If the method returns a *reference*, then it must be preceded by the keyword
-/// `ref`:
+/// If the method returns a *reference*, then it must be preceded by a `*`:
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -58,7 +57,7 @@
 /// }
 ///
 /// # let value = vec![MyStruct { a_field: 100 }];
-/// verify_that!(value, contains(property!(ref MyStruct.get_a_field(), eq(100))))
+/// verify_that!(value, contains(property!(*MyStruct.get_a_field(), eq(100))))
 /// #    .unwrap();
 /// ```
 ///
@@ -93,17 +92,18 @@
 /// }
 ///
 /// let value = MyStruct { a_string: "A string".into() };
-/// verify_that!(value, property!(ref MyStruct.get_a_string(), eq("A string"))) // Does not compile
+/// verify_that!(value, property!(*MyStruct.get_a_string(), eq("A string"))) // Does not compile
 /// #    .unwrap();
 /// ```
 ///
-/// This macro is analogous to [`field`][crate::field], except that it extracts
-/// the datum to be matched from the given object by invoking a method rather
-/// than accessing a field.
+/// This macro is analogous to [`field`][crate::matchers::field], except that it
+/// extracts the datum to be matched from the given object by invoking a method
+/// rather than accessing a field.
 ///
 /// The list of arguments may optionally have a trailing comma.
 #[macro_export]
-macro_rules! property {
+#[doc(hidden)]
+macro_rules! __property {
     ($($t:tt)*) => { $crate::property_internal!($($t)*) }
 }
 
@@ -113,15 +113,15 @@
 #[macro_export]
 macro_rules! property_internal {
     ($($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
-         use $crate::matchers::property_matcher::internal::property_matcher;
+         use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher;
         property_matcher(
             |o: &$($t)::+| o.$method($($argument),*),
             &stringify!($method($($argument),*)),
             $m)
     }};
 
-    (ref $($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
-        use $crate::matchers::property_matcher::internal::property_ref_matcher;
+    (* $($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher;
         property_ref_matcher(
             |o: &$($t)::+| o.$method($($argument),*),
             &stringify!($method($($argument),*)),
@@ -134,7 +134,10 @@
 /// **For internal use only. API stablility is not guaranteed!**
 #[doc(hidden)]
 pub mod internal {
-    use crate::matcher::{Matcher, MatcherResult};
+    use crate::{
+        description::Description,
+        matcher::{Matcher, MatcherResult},
+    };
     use std::{fmt::Debug, marker::PhantomData};
 
     /// **For internal use only. API stablility is not guaranteed!**
@@ -167,15 +170,16 @@
             self.inner.matches(&(self.extractor)(actual))
         }
 
-        fn describe(&self, matcher_result: MatcherResult) -> String {
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
             format!(
                 "has property `{}`, which {}",
                 self.property_desc,
                 self.inner.describe(matcher_result)
             )
+            .into()
         }
 
-        fn explain_match(&self, actual: &OuterT) -> String {
+        fn explain_match(&self, actual: &OuterT) -> Description {
             let actual_inner = (self.extractor)(actual);
             format!(
                 "whose property `{}` is `{:#?}`, {}",
@@ -183,6 +187,7 @@
                 actual_inner,
                 self.inner.explain_match(&actual_inner)
             )
+            .into()
         }
     }
 
@@ -216,15 +221,16 @@
             self.inner.matches((self.extractor)(actual))
         }
 
-        fn describe(&self, matcher_result: MatcherResult) -> String {
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
             format!(
                 "has property `{}`, which {}",
                 self.property_desc,
                 self.inner.describe(matcher_result)
             )
+            .into()
         }
 
-        fn explain_match(&self, actual: &OuterT) -> String {
+        fn explain_match(&self, actual: &OuterT) -> Description {
             let actual_inner = (self.extractor)(actual);
             format!(
                 "whose property `{}` is `{:#?}`, {}",
@@ -232,6 +238,7 @@
                 actual_inner,
                 self.inner.explain_match(actual_inner)
             )
+            .into()
         }
     }
 }
diff --git a/src/matchers/some_matcher.rs b/src/matchers/some_matcher.rs
index a6ce021..905aa17 100644
--- a/src/matchers/some_matcher.rs
+++ b/src/matchers/some_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches an `Option` containing a value matched by `inner`.
@@ -51,24 +54,25 @@
         actual.as_ref().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
     }
 
-    fn explain_match(&self, actual: &Option<T>) -> String {
+    fn explain_match(&self, actual: &Option<T>) -> Description {
         match (self.matches(actual), actual) {
-            (_, Some(t)) => format!("which has a value {}", self.inner.explain_match(t)),
-            (_, None) => "which is None".to_string(),
+            (_, Some(t)) => {
+                Description::new().text("which has a value").nested(self.inner.explain_match(t))
+            }
+            (_, None) => "which is None".into(),
         }
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
             MatcherResult::Match => {
-                format!("has a value which {}", self.inner.describe(MatcherResult::Match))
+                format!("has a value which {}", self.inner.describe(MatcherResult::Match)).into()
             }
-            MatcherResult::NoMatch => {
-                format!(
-                    "is None or has a value which {}",
-                    self.inner.describe(MatcherResult::NoMatch)
-                )
-            }
+            MatcherResult::NoMatch => format!(
+                "is None or has a value which {}",
+                self.inner.describe(MatcherResult::NoMatch)
+            )
+            .into(),
         }
     }
 }
@@ -100,7 +104,7 @@
 
     #[test]
     fn some_does_not_match_option_with_none() -> Result<()> {
-        let matcher = some(eq(1));
+        let matcher = some(eq::<i32, _>(1));
 
         let result = matcher.matches(&None);
 
@@ -117,7 +121,8 @@
                     Value of: Some(2)
                     Expected: has a value which is equal to 1
                     Actual: Some(2),
-                      which has a value which isn't equal to 1
+                      which has a value
+                        which isn't equal to 1
                 "
             ))))
         )
@@ -126,29 +131,29 @@
     #[test]
     fn some_describe_matches() -> Result<()> {
         verify_that!(
-            some(eq(1)).describe(MatcherResult::Match),
-            eq("has a value which is equal to 1")
+            some(eq::<i32, _>(1)).describe(MatcherResult::Match),
+            displays_as(eq("has a value which is equal to 1"))
         )
     }
 
     #[test]
     fn some_describe_does_not_match() -> Result<()> {
         verify_that!(
-            some(eq(1)).describe(MatcherResult::NoMatch),
-            eq("is None or has a value which isn't equal to 1")
+            some(eq::<i32, _>(1)).describe(MatcherResult::NoMatch),
+            displays_as(eq("is None or has a value which isn't equal to 1"))
         )
     }
 
     #[test]
     fn some_explain_match_with_none() -> Result<()> {
-        verify_that!(some(eq(1)).explain_match(&None), displays_as(eq("which is None")))
+        verify_that!(some(eq::<i32, _>(1)).explain_match(&None), displays_as(eq("which is None")))
     }
 
     #[test]
     fn some_explain_match_with_some_success() -> Result<()> {
         verify_that!(
             some(eq(1)).explain_match(&Some(1)),
-            displays_as(eq("which has a value which is equal to 1"))
+            displays_as(eq("which has a value\n  which is equal to 1"))
         )
     }
 
@@ -156,7 +161,7 @@
     fn some_explain_match_with_some_fail() -> Result<()> {
         verify_that!(
             some(eq(1)).explain_match(&Some(2)),
-            displays_as(eq("which has a value which isn't equal to 1"))
+            displays_as(eq("which has a value\n  which isn't equal to 1"))
         )
     }
 }
diff --git a/src/matchers/str_matcher.rs b/src/matchers/str_matcher.rs
index 3a4e2e9..b624d44 100644
--- a/src/matchers/str_matcher.rs
+++ b/src/matchers/str_matcher.rs
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 use crate::{
+    description::Description,
     matcher::{Matcher, MatcherResult},
     matcher_support::{
         edit_distance,
@@ -303,11 +304,11 @@
         self.configuration.do_strings_match(self.expected.deref(), actual.as_ref()).into()
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         self.configuration.describe(matcher_result, self.expected.deref())
     }
 
-    fn explain_match(&self, actual: &ActualT) -> String {
+    fn explain_match(&self, actual: &ActualT) -> Description {
         self.configuration.explain_match(self.expected.deref(), actual.as_ref())
     }
 }
@@ -467,7 +468,7 @@
     }
 
     // StrMatcher::describe redirects immediately to this function.
-    fn describe(&self, matcher_result: MatcherResult, expected: &str) -> String {
+    fn describe(&self, matcher_result: MatcherResult, expected: &str) -> Description {
         let mut addenda: Vec<Cow<'static, str>> = Vec::with_capacity(3);
         match (self.ignore_leading_whitespace, self.ignore_trailing_whitespace) {
             (true, true) => addenda.push("ignoring leading and trailing whitespace".into()),
@@ -502,14 +503,15 @@
                 MatcherResult::NoMatch => "does not end with",
             },
         };
-        format!("{match_mode_description} {expected:?}{extra}")
+        format!("{match_mode_description} {expected:?}{extra}").into()
     }
 
-    fn explain_match(&self, expected: &str, actual: &str) -> String {
+    fn explain_match(&self, expected: &str, actual: &str) -> Description {
         let default_explanation = format!(
             "which {}",
             self.describe(self.do_strings_match(expected, actual).into(), expected)
-        );
+        )
+        .into();
         if !expected.contains('\n') || !actual.contains('\n') {
             return default_explanation;
         }
@@ -549,7 +551,7 @@
             MatchMode::EndsWith => create_diff_reversed(actual, expected, self.mode.to_diff_mode()),
         };
 
-        format!("{default_explanation}\n{diff}",)
+        format!("{default_explanation}\n{diff}").into()
     }
 
     fn ignoring_leading_whitespace(self) -> Self {
@@ -811,7 +813,7 @@
         let matcher: StrMatcher<&str, _> = StrMatcher::with_default_config("A string");
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq("is equal to \"A string\"")
+            displays_as(eq("is equal to \"A string\""))
         )
     }
 
@@ -820,7 +822,7 @@
         let matcher: StrMatcher<&str, _> = StrMatcher::with_default_config("A string");
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::NoMatch),
-            eq("isn't equal to \"A string\"")
+            displays_as(eq("isn't equal to \"A string\""))
         )
     }
 
@@ -830,7 +832,7 @@
             StrMatcher::with_default_config("A string").ignoring_leading_whitespace();
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq("is equal to \"A string\" (ignoring leading whitespace)")
+            displays_as(eq("is equal to \"A string\" (ignoring leading whitespace)"))
         )
     }
 
@@ -840,7 +842,7 @@
             StrMatcher::with_default_config("A string").ignoring_leading_whitespace();
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::NoMatch),
-            eq("isn't equal to \"A string\" (ignoring leading whitespace)")
+            displays_as(eq("isn't equal to \"A string\" (ignoring leading whitespace)"))
         )
     }
 
@@ -850,7 +852,7 @@
             StrMatcher::with_default_config("A string").ignoring_trailing_whitespace();
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq("is equal to \"A string\" (ignoring trailing whitespace)")
+            displays_as(eq("is equal to \"A string\" (ignoring trailing whitespace)"))
         )
     }
 
@@ -861,7 +863,7 @@
             StrMatcher::with_default_config("A string").ignoring_outer_whitespace();
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq("is equal to \"A string\" (ignoring leading and trailing whitespace)")
+            displays_as(eq("is equal to \"A string\" (ignoring leading and trailing whitespace)"))
         )
     }
 
@@ -871,7 +873,7 @@
             StrMatcher::with_default_config("A string").ignoring_ascii_case();
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq("is equal to \"A string\" (ignoring ASCII case)")
+            displays_as(eq("is equal to \"A string\" (ignoring ASCII case)"))
         )
     }
 
@@ -883,7 +885,9 @@
             .ignoring_ascii_case();
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq("is equal to \"A string\" (ignoring leading whitespace, ignoring ASCII case)")
+            displays_as(eq(
+                "is equal to \"A string\" (ignoring leading whitespace, ignoring ASCII case)"
+            ))
         )
     }
 
@@ -892,7 +896,7 @@
         let matcher: StrMatcher<&str, _> = contains_substring("A string");
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq("contains a substring \"A string\"")
+            displays_as(eq("contains a substring \"A string\""))
         )
     }
 
@@ -901,7 +905,7 @@
         let matcher: StrMatcher<&str, _> = contains_substring("A string");
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::NoMatch),
-            eq("does not contain a substring \"A string\"")
+            displays_as(eq("does not contain a substring \"A string\""))
         )
     }
 
@@ -910,7 +914,7 @@
         let matcher: StrMatcher<&str, _> = contains_substring("A string").times(gt(2));
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq("contains a substring \"A string\" (count is greater than 2)")
+            displays_as(eq("contains a substring \"A string\" (count is greater than 2)"))
         )
     }
 
@@ -919,7 +923,7 @@
         let matcher: StrMatcher<&str, _> = starts_with("A string");
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq("starts with prefix \"A string\"")
+            displays_as(eq("starts with prefix \"A string\""))
         )
     }
 
@@ -928,7 +932,7 @@
         let matcher: StrMatcher<&str, _> = starts_with("A string");
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::NoMatch),
-            eq("does not start with \"A string\"")
+            displays_as(eq("does not start with \"A string\""))
         )
     }
 
@@ -937,7 +941,7 @@
         let matcher: StrMatcher<&str, _> = ends_with("A string");
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq("ends with suffix \"A string\"")
+            displays_as(eq("ends with suffix \"A string\""))
         )
     }
 
@@ -946,7 +950,7 @@
         let matcher: StrMatcher<&str, _> = ends_with("A string");
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::NoMatch),
-            eq("does not end with \"A string\"")
+            displays_as(eq("does not end with \"A string\""))
         )
     }
 
@@ -971,14 +975,13 @@
 
         verify_that!(
             result,
-            err(displays_as(contains_substring(indoc!(
-                "
-                 First line
-                -Second line
-                +Second lines
-                 Third line
-                "
-            ))))
+            err(displays_as(contains_substring(
+                "\
+   First line
+  -Second line
+  +Second lines
+   Third line"
+            )))
         )
     }
 
@@ -1004,15 +1007,14 @@
 
         verify_that!(
             result,
-            err(displays_as(contains_substring(indoc!(
+            err(displays_as(contains_substring(
                 "
-                     First line
-                    -Second line
-                    +Second lines
-                     Third line
-                     <---- remaining lines omitted ---->
-                "
-            ))))
+   First line
+  -Second line
+  +Second lines
+   Third line
+   <---- remaining lines omitted ---->"
+            )))
         )
     }
 
@@ -1037,14 +1039,13 @@
 
         verify_that!(
             result,
-            err(displays_as(contains_substring(indoc!(
-                "
-                     First line
-                    -Second line
-                    +Second lines
-                     <---- remaining lines omitted ---->
-                "
-            ))))
+            err(displays_as(contains_substring(
+                "\
+   First line
+  -Second line
+  +Second lines
+   <---- remaining lines omitted ---->"
+            )))
         )
     }
 
@@ -1070,16 +1071,15 @@
 
         verify_that!(
             result,
-            err(displays_as(contains_substring(indoc!(
+            err(displays_as(contains_substring(
                 "
-                    Difference(-actual / +expected):
-                     <---- remaining lines omitted ---->
-                     Second line
-                    +Third lines
-                    -Third line
-                     Fourth line
-                "
-            ))))
+  Difference(-actual / +expected):
+   <---- remaining lines omitted ---->
+   Second line
+  -Third line
+  +Third lines
+   Fourth line"
+            )))
         )
     }
 
@@ -1107,16 +1107,16 @@
 
         verify_that!(
             result,
-            err(displays_as(contains_substring(indoc!(
+            err(displays_as(contains_substring(
                 "
-                    Difference(-actual / +expected):
-                     <---- remaining lines omitted ---->
-                     Second line
-                    +Third lines
-                    -Third line
-                     Fourth line
-                     <---- remaining lines omitted ---->"
-            ))))
+  Difference(-actual / +expected):
+   <---- remaining lines omitted ---->
+   Second line
+  -Third line
+  +Third lines
+   Fourth line
+   <---- remaining lines omitted ---->"
+            )))
         )
     }
 
@@ -1144,20 +1144,19 @@
 
         verify_that!(
             result,
-            err(displays_as(contains_substring(indoc!(
+            err(displays_as(contains_substring(
                 "
-                    Difference(-actual / +expected):
-                     <---- remaining lines omitted ---->
-                    +line
-                    -Second line
-                     Third line
-                    +Foorth line
-                    -Fourth line
-                    +Fifth
-                    -Fifth line
-                     <---- remaining lines omitted ---->
-                "
-            ))))
+  Difference(-actual / +expected):
+   <---- remaining lines omitted ---->
+  -Second line
+  +line
+   Third line
+  -Fourth line
+  +Foorth line
+  -Fifth line
+  +Fifth
+   <---- remaining lines omitted ---->"
+            )))
         )
     }
 
@@ -1183,15 +1182,14 @@
 
         verify_that!(
             result,
-            err(displays_as(contains_substring(indoc!(
-                "
-                     First line
-                    -Second line
-                    +Second lines
-                     Third line
-                    -Fourth line
-                "
-            ))))
+            err(displays_as(contains_substring(
+                "\
+   First line
+  -Second line
+  +Second lines
+   Third line
+  -Fourth line"
+            )))
         )
     }
 
diff --git a/src/matchers/subset_of_matcher.rs b/src/matchers/subset_of_matcher.rs
index facee4f..24c00d8 100644
--- a/src/matchers/subset_of_matcher.rs
+++ b/src/matchers/subset_of_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches a container all of whose items are in the given container
@@ -22,7 +25,8 @@
 /// comparison.
 ///
 /// `ActualT` and `ExpectedT` can each be any container a reference to which
-/// implements `IntoIterator`. They need not be the same container type.
+/// implements `IntoIterator`. For instance, `T` can be a common container like
+/// `Vec` or arrays. They need not be the same container type.
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -30,6 +34,8 @@
 /// # fn should_pass_1() -> Result<()> {
 /// let value = vec![1, 2, 3];
 /// verify_that!(value, subset_of([1, 2, 3, 4]))?;  // Passes
+/// let array_value = [1, 2, 3];
+/// verify_that!(array_value, subset_of([1, 2, 3, 4]))?;  // Passes
 /// #     Ok(())
 /// # }
 /// # fn should_fail() -> Result<()> {
@@ -109,7 +115,7 @@
         MatcherResult::Match
     }
 
-    fn explain_match(&self, actual: &ActualT) -> String {
+    fn explain_match(&self, actual: &ActualT) -> Description {
         let unexpected_elements = actual
             .into_iter()
             .enumerate()
@@ -118,16 +124,16 @@
             .collect::<Vec<_>>();
 
         match unexpected_elements.len() {
-            0 => "which no element is unexpected".to_string(),
-            1 => format!("whose element {} is unexpected", &unexpected_elements[0]),
-            _ => format!("whose elements {} are unexpected", unexpected_elements.join(", ")),
+            0 => "which no element is unexpected".into(),
+            1 => format!("whose element {} is unexpected", &unexpected_elements[0]).into(),
+            _ => format!("whose elements {} are unexpected", unexpected_elements.join(", ")).into(),
         }
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
-            MatcherResult::Match => format!("is a subset of {:#?}", self.superset),
-            MatcherResult::NoMatch => format!("isn't a subset of {:#?}", self.superset),
+            MatcherResult::Match => format!("is a subset of {:#?}", self.superset).into(),
+            MatcherResult::NoMatch => format!("isn't a subset of {:#?}", self.superset).into(),
         }
     }
 }
diff --git a/src/matchers/superset_of_matcher.rs b/src/matchers/superset_of_matcher.rs
index 8e98015..d1e9d72 100644
--- a/src/matchers/superset_of_matcher.rs
+++ b/src/matchers/superset_of_matcher.rs
@@ -12,7 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use crate::matcher::{Matcher, MatcherResult};
+use crate::{
+    description::Description,
+    matcher::{Matcher, MatcherResult},
+};
 use std::{fmt::Debug, marker::PhantomData};
 
 /// Matches a container containing all of the items in the given container
@@ -22,7 +25,9 @@
 /// comparison.
 ///
 /// `ActualT` and `ExpectedT` can each be any container a reference to which
-/// implements `IntoIterator`. They need not be the same container type.
+/// implements `IntoIterator`. For instance, `ActualT` and `ExpectedT` can be a
+/// common container like `Vec` or arrays. They need not be the same container
+/// type.
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -30,6 +35,8 @@
 /// # fn should_pass_1() -> Result<()> {
 /// let value = vec![1, 2, 3];
 /// verify_that!(value, superset_of([1, 2]))?;  // Passes
+/// let array_value = [1, 2, 3];
+/// verify_that!(array_value, superset_of([1, 2]))?;  // Passes
 /// #     Ok(())
 /// # }
 /// # fn should_fail() -> Result<()> {
@@ -109,7 +116,7 @@
         MatcherResult::Match
     }
 
-    fn explain_match(&self, actual: &ActualT) -> String {
+    fn explain_match(&self, actual: &ActualT) -> Description {
         let missing_items: Vec<_> = self
             .subset
             .into_iter()
@@ -117,16 +124,16 @@
             .map(|expected_item| format!("{expected_item:#?}"))
             .collect();
         match missing_items.len() {
-            0 => "whose no element is missing".to_string(),
-            1 => format!("whose element {} is missing", &missing_items[0]),
-            _ => format!("whose elements {} are missing", missing_items.join(", ")),
+            0 => "whose no element is missing".into(),
+            1 => format!("whose element {} is missing", &missing_items[0]).into(),
+            _ => format!("whose elements {} are missing", missing_items.join(", ")).into(),
         }
     }
 
-    fn describe(&self, matcher_result: MatcherResult) -> String {
+    fn describe(&self, matcher_result: MatcherResult) -> Description {
         match matcher_result {
-            MatcherResult::Match => format!("is a superset of {:#?}", self.subset),
-            MatcherResult::NoMatch => format!("isn't a superset of {:#?}", self.subset),
+            MatcherResult::Match => format!("is a superset of {:#?}", self.subset).into(),
+            MatcherResult::NoMatch => format!("isn't a superset of {:#?}", self.subset).into(),
         }
     }
 }
diff --git a/src/matchers/tuple_matcher.rs b/src/matchers/tuple_matcher.rs
index a2e325b..af55cbf 100644
--- a/src/matchers/tuple_matcher.rs
+++ b/src/matchers/tuple_matcher.rs
@@ -21,22 +21,11 @@
 /// **For internal use only. API stablility is not guaranteed!**
 #[doc(hidden)]
 pub mod internal {
-    use crate::matcher::{Matcher, MatcherResult};
-    use std::fmt::{Debug, Write};
-
-    /// Replaces the first expression with the second at compile time.
-    ///
-    /// This is used below in repetition sequences where the output must only
-    /// include the same expression repeated the same number of times as the
-    /// macro input.
-    ///
-    /// **For internal use only. API stablility is not guaranteed!**
-    #[doc(hidden)]
-    macro_rules! replace_expr {
-        ($_ignored:tt, $replacement:expr) => {
-            $replacement
-        };
-    }
+    use crate::{
+        description::Description,
+        matcher::{Matcher, MatcherResult},
+    };
+    use std::fmt::Debug;
 
     // This implementation is provided for completeness, but is completely trivial.
     // The only actual value which can be supplied is (), which must match.
@@ -47,7 +36,7 @@
             MatcherResult::Match
         }
 
-        fn describe(&self, matcher_result: MatcherResult) -> String {
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
             match matcher_result {
                 MatcherResult::Match => "is the empty tuple".into(),
                 MatcherResult::NoMatch => "is not the empty tuple".into(),
@@ -76,41 +65,30 @@
                     MatcherResult::Match
                 }
 
-                fn explain_match(&self, actual: &($($field_type,)*)) -> String {
-                    let mut explanation = format!("which {}", self.describe(self.matches(actual)));
+                fn explain_match(&self, actual: &($($field_type,)*)) -> Description {
+                    let mut explanation = Description::new().text("which").nested(self.describe(self.matches(actual)));
                     $(match self.$field_number.matches(&actual.$field_number) {
                         MatcherResult::Match => {},
                         MatcherResult::NoMatch => {
-                            writeln!(
-                                &mut explanation,
-                                concat!("Element #", $field_number, " is {:?}, {}"),
-                                actual.$field_number,
-                                self.$field_number.explain_match(&actual.$field_number)
-                            ).unwrap();
+                            explanation = explanation
+                                .text(format!(concat!("Element #", $field_number, " is {:?},"), actual.$field_number))
+                                .nested(self.$field_number.explain_match(&actual.$field_number));
                         }
                     })*
-                    (explanation)
+                    explanation
                 }
 
-                fn describe(&self, matcher_result: MatcherResult) -> String {
+                fn describe(&self, matcher_result: MatcherResult) -> Description {
                     match matcher_result {
                         MatcherResult::Match => {
-                            format!(
-                                concat!(
-                                    "is a tuple whose values respectively match:\n",
-                                    $(replace_expr!($field_number, "  {},\n")),*
-                                ),
-                                $(self.$field_number.describe(matcher_result)),*
-                            )
+                            let mut description = Description::new().text("is a tuple whose values respectively match:");
+                            $(description = description.nested(self.$field_number.describe(matcher_result));)*
+                            description
                         }
                         MatcherResult::NoMatch => {
-                            format!(
-                                concat!(
-                                    "is a tuple whose values do not respectively match:\n",
-                                    $(replace_expr!($field_number, "  {},\n")),*
-                                ),
-                                $(self.$field_number.describe(MatcherResult::Match)),*
-                            )
+                            let mut description = Description::new().text("is a tuple whose values do not respectively match:");
+                            $(description = description.nested(self.$field_number.describe(MatcherResult::Match));)*
+                            description
                         }
                     }
                 }
diff --git a/src/matchers/unordered_elements_are_matcher.rs b/src/matchers/unordered_elements_are_matcher.rs
index 1930697..f4585a4 100644
--- a/src/matchers/unordered_elements_are_matcher.rs
+++ b/src/matchers/unordered_elements_are_matcher.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // There are no visible documentation elements in this module; the declarative
-// macro is documented at the top level.
+// macro is documented in the matchers module.
 #![doc(hidden)]
 
 /// Matches a container whose elements in any order have a 1:1 correspondence
@@ -43,8 +43,9 @@
 /// # should_fail_3().unwrap_err();
 /// ```
 ///
-/// The actual value must be a container implementing [`IntoIterator`]. This
-/// includes standard containers, slices (when dereferenced) and arrays.
+/// The actual value must be a container such as a `Vec`, an array, or a
+/// dereferenced slice. More precisely, a shared borrow of the actual value must
+/// implement [`IntoIterator`].
 ///
 /// This can also match against [`HashMap`][std::collections::HashMap] and
 /// similar collections. The arguments are a sequence of pairs of matchers
@@ -73,7 +74,7 @@
 ///
 /// Note: This behavior is only possible in [`verify_that!`] macros. In any
 /// other cases, it is still necessary to use the
-/// [`unordered_elements_are!`][crate::unordered_elements_are] macro.
+/// [`unordered_elements_are!`][crate::matchers::unordered_elements_are] macro.
 ///
 /// ```compile_fail
 /// # use googletest::prelude::*;
@@ -115,9 +116,10 @@
 /// [`Iterator::collect`]: std::iter::Iterator::collect
 /// [`Vec`]: std::vec::Vec
 #[macro_export]
-macro_rules! unordered_elements_are {
+#[doc(hidden)]
+macro_rules! __unordered_elements_are {
     ($(,)?) => {{
-        use $crate::matchers::unordered_elements_are_matcher::internal::{
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
             UnorderedElementsAreMatcher, Requirements
         };
         UnorderedElementsAreMatcher::new([], Requirements::PerfectMatch)
@@ -126,7 +128,7 @@
     // TODO: Consider an alternative map-like syntax here similar to that used in
     // https://crates.io/crates/maplit.
     ($(($key_matcher:expr, $value_matcher:expr)),* $(,)?) => {{
-        use $crate::matchers::unordered_elements_are_matcher::internal::{
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
             UnorderedElementsOfMapAreMatcher, Requirements
         };
         UnorderedElementsOfMapAreMatcher::new(
@@ -136,7 +138,7 @@
     }};
 
     ($($matcher:expr),* $(,)?) => {{
-        use $crate::matchers::unordered_elements_are_matcher::internal::{
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
             UnorderedElementsAreMatcher, Requirements
         };
         UnorderedElementsAreMatcher::new([$(Box::new($matcher)),*], Requirements::PerfectMatch)
@@ -151,7 +153,9 @@
 /// additional elements that don't correspond to any matcher.
 ///
 /// Put another way, `contains_each!` matches if there is a subset of the actual
-/// container which [`unordered_elements_are`] would match.
+/// container which
+/// [`unordered_elements_are`][crate::matchers::unordered_elements_are] would
+/// match.
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -178,8 +182,9 @@
 /// # should_fail_3().unwrap_err();
 /// ```
 ///
-/// The actual value must be a container implementing [`IntoIterator`]. This
-/// includes standard containers, slices (when dereferenced) and arrays.
+/// The actual value must be a container such as a `Vec`, an array, or a
+/// dereferenced slice. More precisely, a shared borrow of the actual value must
+/// implement [`IntoIterator`].
 ///
 /// This can also match against [`HashMap`][std::collections::HashMap] and
 /// similar collections. The arguments are a sequence of pairs of matchers
@@ -217,9 +222,10 @@
 /// [`Iterator::collect`]: std::iter::Iterator::collect
 /// [`Vec`]: std::vec::Vec
 #[macro_export]
-macro_rules! contains_each {
+#[doc(hidden)]
+macro_rules! __contains_each {
     ($(,)?) => {{
-        use $crate::matchers::unordered_elements_are_matcher::internal::{
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
             UnorderedElementsAreMatcher, Requirements
         };
         UnorderedElementsAreMatcher::new([], Requirements::Superset)
@@ -228,7 +234,7 @@
     // TODO: Consider an alternative map-like syntax here similar to that used in
     // https://crates.io/crates/maplit.
     ($(($key_matcher:expr, $value_matcher:expr)),* $(,)?) => {{
-        use $crate::matchers::unordered_elements_are_matcher::internal::{
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
             UnorderedElementsOfMapAreMatcher, Requirements
         };
         UnorderedElementsOfMapAreMatcher::new(
@@ -238,7 +244,7 @@
     }};
 
     ($($matcher:expr),* $(,)?) => {{
-        use $crate::matchers::unordered_elements_are_matcher::internal::{
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
             UnorderedElementsAreMatcher, Requirements
         };
         UnorderedElementsAreMatcher::new([$(Box::new($matcher)),*], Requirements::Superset)
@@ -255,7 +261,8 @@
 /// container.
 ///
 /// Put another way, `is_contained_in!` matches if there is a subset of the
-/// matchers which would match with [`unordered_elements_are`].
+/// matchers which would match with
+/// [`unordered_elements_are`][crate::matchers::unordered_elements_are].
 ///
 /// ```
 /// # use googletest::prelude::*;
@@ -282,8 +289,9 @@
 /// # should_fail_3().unwrap_err();
 /// ```
 ///
-/// The actual value must be a container implementing [`IntoIterator`]. This
-/// includes standard containers, slices (when dereferenced) and arrays.
+/// The actual value must be a container such as a `Vec`, an array, or a
+/// dereferenced slice. More precisely, a shared borrow of the actual value must
+/// implement [`IntoIterator`].
 ///
 /// This can also match against [`HashMap`][std::collections::HashMap] and
 /// similar collections. The arguments are a sequence of pairs of matchers
@@ -323,9 +331,10 @@
 /// [`Iterator::collect`]: std::iter::Iterator::collect
 /// [`Vec`]: std::vec::Vec
 #[macro_export]
-macro_rules! is_contained_in {
+#[doc(hidden)]
+macro_rules! __is_contained_in {
     ($(,)?) => {{
-        use $crate::matchers::unordered_elements_are_matcher::internal::{
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
             UnorderedElementsAreMatcher, Requirements
         };
         UnorderedElementsAreMatcher::new([], Requirements::Subset)
@@ -334,7 +343,7 @@
     // TODO: Consider an alternative map-like syntax here similar to that used in
     // https://crates.io/crates/maplit.
     ($(($key_matcher:expr, $value_matcher:expr)),* $(,)?) => {{
-        use $crate::matchers::unordered_elements_are_matcher::internal::{
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
             UnorderedElementsOfMapAreMatcher, Requirements
         };
         UnorderedElementsOfMapAreMatcher::new(
@@ -344,7 +353,7 @@
     }};
 
     ($($matcher:expr),* $(,)?) => {{
-        use $crate::matchers::unordered_elements_are_matcher::internal::{
+        use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
             UnorderedElementsAreMatcher, Requirements
         };
         UnorderedElementsAreMatcher::new([$(Box::new($matcher)),*], Requirements::Subset)
@@ -356,9 +365,9 @@
 /// **For internal use only. API stablility is not guaranteed!**
 #[doc(hidden)]
 pub mod internal {
+    use crate::description::Description;
     use crate::matcher::{Matcher, MatcherResult};
     use crate::matcher_support::count_elements::count_elements;
-    use crate::matcher_support::description::Description;
     use std::collections::HashSet;
     use std::fmt::{Debug, Display};
     use std::marker::PhantomData;
@@ -406,7 +415,7 @@
             match_matrix.is_match_for(self.requirements).into()
         }
 
-        fn explain_match(&self, actual: &ContainerT) -> String {
+        fn explain_match(&self, actual: &ContainerT) -> Description {
             if let Some(size_mismatch_explanation) =
                 self.requirements.explain_size_mismatch(actual, N)
             {
@@ -423,10 +432,10 @@
             let best_match = match_matrix.find_best_match();
             best_match
                 .get_explanation(actual, &self.elements, self.requirements)
-                .unwrap_or("whose elements all match".to_string())
+                .unwrap_or("whose elements all match".into())
         }
 
-        fn describe(&self, matcher_result: MatcherResult) -> String {
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
             format!(
                 "{} elements matching in any order:\n{}",
                 if matcher_result.into() { "contains" } else { "doesn't contain" },
@@ -437,6 +446,7 @@
                     .enumerate()
                     .indent()
             )
+            .into()
         }
     }
 
@@ -482,7 +492,7 @@
             match_matrix.is_match_for(self.requirements).into()
         }
 
-        fn explain_match(&self, actual: &ContainerT) -> String {
+        fn explain_match(&self, actual: &ContainerT) -> Description {
             if let Some(size_mismatch_explanation) =
                 self.requirements.explain_size_mismatch(actual, N)
             {
@@ -500,10 +510,10 @@
 
             best_match
                 .get_explanation_for_map(actual, &self.elements, self.requirements)
-                .unwrap_or("whose elements all match".to_string())
+                .unwrap_or("whose elements all match".into())
         }
 
-        fn describe(&self, matcher_result: MatcherResult) -> String {
+        fn describe(&self, matcher_result: MatcherResult) -> Description {
             format!(
                 "{} elements matching in any order:\n{}",
                 if matcher_result.into() { "contains" } else { "doesn't contain" },
@@ -517,6 +527,7 @@
                     .collect::<Description>()
                     .indent()
             )
+            .into()
         }
     }
 
@@ -545,25 +556,25 @@
             &self,
             actual: &ContainerT,
             expected_size: usize,
-        ) -> Option<String>
+        ) -> Option<Description>
         where
             for<'b> &'b ContainerT: IntoIterator,
         {
             let actual_size = count_elements(actual);
             match self {
-                Requirements::PerfectMatch if actual_size != expected_size => {
-                    Some(format!("which has size {} (expected {})", actual_size, expected_size))
-                }
+                Requirements::PerfectMatch if actual_size != expected_size => Some(
+                    format!("which has size {} (expected {})", actual_size, expected_size).into(),
+                ),
 
-                Requirements::Superset if actual_size < expected_size => Some(format!(
-                    "which has size {} (expected at least {})",
-                    actual_size, expected_size
-                )),
+                Requirements::Superset if actual_size < expected_size => Some(
+                    format!("which has size {} (expected at least {})", actual_size, expected_size)
+                        .into(),
+                ),
 
-                Requirements::Subset if actual_size > expected_size => Some(format!(
-                    "which has size {} (expected at most {})",
-                    actual_size, expected_size
-                )),
+                Requirements::Subset if actual_size > expected_size => Some(
+                    format!("which has size {} (expected at most {})", actual_size, expected_size)
+                        .into(),
+                ),
 
                 _ => None,
             }
@@ -641,7 +652,7 @@
             }
         }
 
-        fn explain_unmatchable(&self, requirements: Requirements) -> Option<String> {
+        fn explain_unmatchable(&self, requirements: Requirements) -> Option<Description> {
             let unmatchable_elements = match requirements {
                 Requirements::PerfectMatch => self.find_unmatchable_elements(),
                 Requirements::Superset => self.find_unmatched_expected(),
@@ -848,7 +859,7 @@
                 || self.unmatchable_expected.iter().any(|b| *b)
         }
 
-        fn get_explanation(&self) -> Option<String> {
+        fn get_explanation(&self) -> Option<Description> {
             let unmatchable_actual = self.unmatchable_actual();
             let actual_idx = unmatchable_actual
                 .iter()
@@ -864,29 +875,29 @@
             match (unmatchable_actual.len(), unmatchable_expected.len()) {
                 (0, 0) => None,
                 (1, 0) => {
-                    Some(format!("whose element {actual_idx} does not match any expected elements"))
+                    Some(format!("whose element {actual_idx} does not match any expected elements").into())
                 }
                 (_, 0) => {
-                    Some(format!("whose elements {actual_idx} do not match any expected elements",))
+                    Some(format!("whose elements {actual_idx} do not match any expected elements",).into())
                 }
                 (0, 1) => Some(format!(
                     "which has no element matching the expected element {expected_idx}"
-                )),
+                ).into()),
                 (0, _) => Some(format!(
                     "which has no elements matching the expected elements {expected_idx}"
-                )),
+                ).into()),
                 (1, 1) => Some(format!(
                     "whose element {actual_idx} does not match any expected elements and no elements match the expected element {expected_idx}"
-                )),
+                ).into()),
                 (_, 1) => Some(format!(
                     "whose elements {actual_idx} do not match any expected elements and no elements match the expected element {expected_idx}"
-                )),
+                ).into()),
                 (1, _) => Some(format!(
                     "whose element {actual_idx} does not match any expected elements and no elements match the expected elements {expected_idx}"
-                )),
+                ).into()),
                 (_, _) => Some(format!(
                     "whose elements {actual_idx} do not match any expected elements and no elements match the expected elements {expected_idx}"
-                )),
+                ).into()),
             }
         }
 
@@ -953,7 +964,7 @@
             actual: &ContainerT,
             expected: &[Box<dyn Matcher<ActualT = T> + 'a>; N],
             requirements: Requirements,
-        ) -> Option<String>
+        ) -> Option<Description>
         where
             for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
         {
@@ -992,7 +1003,7 @@
                 .indent();
             Some(format!(
                 "which does not have a {requirements} match with the expected elements. The best match found was:\n{best_match}"
-            ))
+            ).into())
         }
 
         fn get_explanation_for_map<'a, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized>(
@@ -1000,7 +1011,7 @@
             actual: &ContainerT,
             expected: &[KeyValueMatcher<'a, KeyT, ValueT>; N],
             requirements: Requirements,
-        ) -> Option<String>
+        ) -> Option<Description>
         where
             for<'b> &'b ContainerT: IntoIterator<Item = (&'b KeyT, &'b ValueT)>,
         {
@@ -1050,7 +1061,7 @@
                 .indent();
             Some(format!(
                 "which does not have a {requirements} match with the expected elements. The best match found was:\n{best_match}"
-            ))
+            ).into())
         }
     }
 }
@@ -1078,13 +1089,13 @@
         ];
         verify_that!(
             Matcher::describe(&matcher, MatcherResult::Match),
-            eq(indoc!(
+            displays_as(eq(indoc!(
                 "
                 contains elements matching in any order:
                   is equal to 2 => is equal to \"Two\"
                   is equal to 1 => is equal to \"One\"
                   is equal to 3 => is equal to \"Three\""
-            ))
+            )))
         )
     }
 
diff --git a/tests/all_matcher_test.rs b/tests/all_matcher_test.rs
index 6d7e3f8..8b36fc0 100644
--- a/tests/all_matcher_test.rs
+++ b/tests/all_matcher_test.rs
@@ -61,9 +61,7 @@
 fn mismatch_description_two_failed_matchers() -> Result<()> {
     verify_that!(
         all!(starts_with("One"), starts_with("Two")).explain_match("Three"),
-        displays_as(eq(
-            "* which does not start with \"One\"\n  * which does not start with \"Two\""
-        ))
+        displays_as(eq("* which does not start with \"One\"\n* which does not start with \"Two\""))
     )
 }
 
@@ -91,3 +89,67 @@
         ))))
     )
 }
+
+#[test]
+fn formats_error_message_correctly_when_all_is_inside_some() -> Result<()> {
+    let value = Some(4);
+    let result = verify_that!(value, some(all![eq(1), eq(2), eq(3)]));
+    verify_that!(
+        result,
+        err(displays_as(contains_substring(indoc!(
+            "
+            Value of: value
+            Expected: has a value which has all the following properties:
+              * is equal to 1
+              * is equal to 2
+              * is equal to 3
+            Actual: Some(4),
+              which has a value
+                * which isn't equal to 1
+                * which isn't equal to 2
+                * which isn't equal to 3"
+        ))))
+    )
+}
+
+#[test]
+fn formats_error_message_correctly_when_all_is_inside_ok() -> Result<()> {
+    let value: std::result::Result<i32, std::io::Error> = Ok(4);
+    let result = verify_that!(value, ok(all![eq(1), eq(2), eq(3)]));
+    verify_that!(
+        result,
+        err(displays_as(contains_substring(indoc!(
+            "
+            Value of: value
+            Expected: is a success containing a value, which has all the following properties:
+              * is equal to 1
+              * is equal to 2
+              * is equal to 3
+            Actual: Ok(4),
+              which is a success
+                * which isn't equal to 1
+                * which isn't equal to 2
+                * which isn't equal to 3"
+        ))))
+    )
+}
+
+#[test]
+fn formats_error_message_correctly_when_all_is_inside_err() -> Result<()> {
+    let value: std::result::Result<(), &'static str> = Err("An error");
+    let result = verify_that!(value, err(all![starts_with("Not"), ends_with("problem")]));
+    verify_that!(
+        result,
+        err(displays_as(contains_substring(indoc!(
+            r#"
+            Value of: value
+            Expected: is an error which has all the following properties:
+              * starts with prefix "Not"
+              * ends with suffix "problem"
+            Actual: Err("An error"),
+              which is an error
+                * which does not start with "Not"
+                * which does not end with "problem""#
+        ))))
+    )
+}
diff --git a/tests/any_matcher_test.rs b/tests/any_matcher_test.rs
index 1bdd794..82ed046 100644
--- a/tests/any_matcher_test.rs
+++ b/tests/any_matcher_test.rs
@@ -61,9 +61,7 @@
 fn mismatch_description_two_failed_matchers() -> Result<()> {
     verify_that!(
         any!(starts_with("One"), starts_with("Two")).explain_match("Three"),
-        displays_as(eq(
-            "* which does not start with \"One\"\n  * which does not start with \"Two\""
-        ))
+        displays_as(eq("* which does not start with \"One\"\n* which does not start with \"Two\""))
     )
 }
 
@@ -91,3 +89,67 @@
         ))))
     )
 }
+
+#[test]
+fn formats_error_message_correctly_when_any_is_inside_some() -> Result<()> {
+    let value = Some(4);
+    let result = verify_that!(value, some(any![eq(1), eq(2), eq(3)]));
+    verify_that!(
+        result,
+        err(displays_as(contains_substring(indoc!(
+            "
+            Value of: value
+            Expected: has a value which has at least one of the following properties:
+              * is equal to 1
+              * is equal to 2
+              * is equal to 3
+            Actual: Some(4),
+              which has a value
+                * which isn't equal to 1
+                * which isn't equal to 2
+                * which isn't equal to 3"
+        ))))
+    )
+}
+
+#[test]
+fn formats_error_message_correctly_when_any_is_inside_ok() -> Result<()> {
+    let value: std::result::Result<i32, std::io::Error> = Ok(4);
+    let result = verify_that!(value, ok(any![eq(1), eq(2), eq(3)]));
+    verify_that!(
+        result,
+        err(displays_as(contains_substring(indoc!(
+            "
+            Value of: value
+            Expected: is a success containing a value, which has at least one of the following properties:
+              * is equal to 1
+              * is equal to 2
+              * is equal to 3
+            Actual: Ok(4),
+              which is a success
+                * which isn't equal to 1
+                * which isn't equal to 2
+                * which isn't equal to 3"
+        ))))
+    )
+}
+
+#[test]
+fn formats_error_message_correctly_when_any_is_inside_err() -> Result<()> {
+    let value: std::result::Result<(), &'static str> = Err("An error");
+    let result = verify_that!(value, err(any![starts_with("Not"), ends_with("problem")]));
+    verify_that!(
+        result,
+        err(displays_as(contains_substring(indoc!(
+            r#"
+            Value of: value
+            Expected: is an error which has at least one of the following properties:
+              * starts with prefix "Not"
+              * ends with suffix "problem"
+            Actual: Err("An error"),
+              which is an error
+                * which does not start with "Not"
+                * which does not end with "problem""#
+        ))))
+    )
+}
diff --git a/tests/colorized_diff_test.rs b/tests/colorized_diff_test.rs
index d1b4ecb..d056020 100644
--- a/tests/colorized_diff_test.rs
+++ b/tests/colorized_diff_test.rs
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 use googletest::prelude::*;
-use indoc::indoc;
 use std::fmt::{Display, Write};
 
 // Make a long text with each element of the iterator on one line.
@@ -37,16 +36,15 @@
 
     verify_that!(
         result,
-        err(displays_as(contains_substring(indoc! {
+        err(displays_as(contains_substring(
             "
-
-            Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
-             1
-             2
-             \x1B[3m<---- 45 common lines omitted ---->\x1B[0m
-             48
-             49
-            +\x1B[1;32m50\x1B[0m"
-        })))
+  Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
+   1
+   2
+   \x1B[3m<---- 45 common lines omitted ---->\x1B[0m
+   48
+   49
+  +\x1B[1;32m50\x1B[0m"
+        )))
     )
 }
diff --git a/tests/elements_are_matcher_test.rs b/tests/elements_are_matcher_test.rs
index 99e9fe7..4de2314 100644
--- a/tests/elements_are_matcher_test.rs
+++ b/tests/elements_are_matcher_test.rs
@@ -82,7 +82,6 @@
         result,
         err(displays_as(contains_substring(indoc!(
             "
-                Value of: vec![vec! [0, 1], vec! [1, 2]]
                 Expected: has elements:
                   0. has elements:
                        0. is equal to 1
@@ -92,12 +91,12 @@
                        1. is equal to 3
                 Actual: [[0, 1], [1, 2]],
                   where:
-                  * element #0 is [0, 1], where:
-                      * element #0 is 0, which isn't equal to 1
-                      * element #1 is 1, which isn't equal to 2
-                  * element #1 is [1, 2], where:
-                      * element #0 is 1, which isn't equal to 2
-                      * element #1 is 2, which isn't equal to 3"
+                    * element #0 is [0, 1], where:
+                        * element #0 is 0, which isn't equal to 1
+                        * element #1 is 1, which isn't equal to 2
+                    * element #1 is [1, 2], where:
+                        * element #0 is 1, which isn't equal to 2
+                        * element #1 is 2, which isn't equal to 3"
         ))))
     )
 }
diff --git a/tests/field_matcher_test.rs b/tests/field_matcher_test.rs
index f5d85c5..f585c21 100644
--- a/tests/field_matcher_test.rs
+++ b/tests/field_matcher_test.rs
@@ -41,7 +41,7 @@
 
     verify_that!(
         matcher.describe(MatcherResult::Match),
-        eq("has field `int`, which is equal to 31")
+        displays_as(eq("has field `int`, which is equal to 31"))
     )
 }
 
diff --git a/tests/matches_pattern_test.rs b/tests/matches_pattern_test.rs
index 4904cf5..3298e36 100644
--- a/tests/matches_pattern_test.rs
+++ b/tests/matches_pattern_test.rs
@@ -202,8 +202,6 @@
     verify_that!(
         result,
         err(displays_as(contains_substring(indoc! {"
-            Value of: actual
-            Expected: is AnEnum :: B which has field `0`, which is equal to 123
             Actual: A(123),
               which has the wrong enum variant `A`
             "
@@ -399,6 +397,12 @@
     verify_that!(actual, matches_pattern!(AnEnum::A))
 }
 
+#[rustversion::before(1.76)]
+const ANENUM_A_REPR: &str = "AnEnum :: A";
+
+#[rustversion::since(1.76)]
+const ANENUM_A_REPR: &str = "AnEnum::A";
+
 #[test]
 fn generates_correct_failure_output_when_enum_variant_without_field_is_not_matched() -> Result<()> {
     #[derive(Debug)]
@@ -411,7 +415,7 @@
 
     let result = verify_that!(actual, matches_pattern!(AnEnum::A));
 
-    verify_that!(result, err(displays_as(contains_substring("is not AnEnum :: A"))))
+    verify_that!(result, err(displays_as(contains_substring(format!("is not {ANENUM_A_REPR}")))))
 }
 
 #[test]
@@ -424,7 +428,7 @@
 
     let result = verify_that!(actual, not(matches_pattern!(AnEnum::A)));
 
-    verify_that!(result, err(displays_as(contains_substring("is AnEnum :: A"))))
+    verify_that!(result, err(displays_as(contains_substring(format!("is {ANENUM_A_REPR}")))))
 }
 
 #[test]
@@ -463,7 +467,9 @@
 
     verify_that!(
         result,
-        err(displays_as(contains_substring("Expected: is AnEnum :: A which has field `0`")))
+        err(displays_as(contains_substring(format!(
+            "Expected: is {ANENUM_A_REPR} which has field `0`"
+        ))))
     )
 }
 
@@ -479,9 +485,9 @@
 
     verify_that!(
         result,
-        err(displays_as(contains_substring(
-            "Expected: is not AnEnum :: A which has field `0`, which is equal to"
-        )))
+        err(displays_as(contains_substring(format!(
+            "Expected: is not {ANENUM_A_REPR} which has field `0`, which is equal to"
+        ))))
     )
 }
 
@@ -497,9 +503,9 @@
 
     verify_that!(
         result,
-        err(displays_as(contains_substring(
-            "Expected: is AnEnum :: A which has all the following properties"
-        )))
+        err(displays_as(contains_substring(format!(
+            "Expected: is {ANENUM_A_REPR} which has all the following properties"
+        ))))
     )
 }
 
@@ -515,9 +521,9 @@
 
     verify_that!(
         result,
-        err(displays_as(contains_substring(
-            "Expected: is AnEnum :: A which has all the following properties"
-        )))
+        err(displays_as(contains_substring(format!(
+            "Expected: is {ANENUM_A_REPR} which has all the following properties"
+        ))))
     )
 }
 
@@ -533,7 +539,9 @@
 
     verify_that!(
         result,
-        err(displays_as(contains_substring("Expected: is AnEnum :: A which has field `field`")))
+        err(displays_as(contains_substring(format!(
+            "Expected: is {ANENUM_A_REPR} which has field `field`"
+        ))))
     )
 }
 
@@ -552,9 +560,9 @@
 
     verify_that!(
         result,
-        err(displays_as(contains_substring(
-            "Expected: is AnEnum :: A which has all the following properties"
-        )))
+        err(displays_as(contains_substring(format!(
+            "Expected: is {ANENUM_A_REPR} which has all the following properties"
+        ))))
     )
 }
 
@@ -594,7 +602,7 @@
     }
     let actual = AStruct { field: 123 };
 
-    let result = verify_that!(actual, matches_pattern!(AStruct { ref get_field(): eq(234) }));
+    let result = verify_that!(actual, matches_pattern!(AStruct { *get_field(): eq(234) }));
 
     verify_that!(
         result,
@@ -641,10 +649,8 @@
     }
     let actual = AStruct { field: 123 };
 
-    let result = verify_that!(
-        actual,
-        matches_pattern!(AStruct { field: eq(123), ref get_field(): eq(234) })
-    );
+    let result =
+        verify_that!(actual, matches_pattern!(AStruct { field: eq(123), *get_field(): eq(234) }));
 
     verify_that!(
         result,
@@ -781,7 +787,7 @@
 
     let actual = AStruct { a_field: 123 };
 
-    verify_that!(actual, matches_pattern!(AStruct { ref get_field_ref(): eq(123) }))
+    verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(): eq(123) }))
 }
 
 #[test]
@@ -799,7 +805,7 @@
 
     let actual = AStruct { a_field: 123 };
 
-    verify_that!(actual, matches_pattern!(AStruct { ref get_field_ref(): eq(123), }))
+    verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(): eq(123), }))
 }
 
 #[test]
@@ -817,7 +823,7 @@
 
     let actual = AStruct { a_field: 1 };
 
-    verify_that!(actual, matches_pattern!(AStruct { ref get_field_ref(2, 3): eq(1) }))
+    verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(2, 3): eq(1) }))
 }
 
 #[test]
@@ -839,7 +845,7 @@
 
     let actual = AStruct { a_field: 1 };
 
-    verify_that!(actual, matches_pattern!(AStruct { ref get_field_ref(AnEnum::AVariant): eq(1) }))
+    verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(AnEnum::AVariant): eq(1) }))
 }
 
 #[test]
@@ -857,7 +863,7 @@
 
     let actual = AStruct { a_field: 1 };
 
-    verify_that!(actual, matches_pattern!(AStruct { ref get_field_ref(2, 3,): eq(1) }))
+    verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(2, 3,): eq(1) }))
 }
 
 #[test]
@@ -990,7 +996,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { ref get_field_ref(): eq(123), another_field: eq(234) })
+        matches_pattern!(AStruct { *get_field_ref(): eq(123), another_field: eq(234) })
     )
 }
 
@@ -1013,7 +1019,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { ref get_field_ref(): eq(123), another_field: eq(234), })
+        matches_pattern!(AStruct { *get_field_ref(): eq(123), another_field: eq(234), })
     )
 }
 
@@ -1035,7 +1041,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { ref get_field_ref(2, 3): eq(1), another_field: eq(123) })
+        matches_pattern!(AStruct { *get_field_ref(2, 3): eq(1), another_field: eq(123) })
     )
 }
 
@@ -1061,7 +1067,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { ref get_field_ref(AnEnum::AVariant): eq(1), another_field: eq(2) })
+        matches_pattern!(AStruct { *get_field_ref(AnEnum::AVariant): eq(1), another_field: eq(2) })
     )
 }
 
@@ -1084,7 +1090,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { ref get_field_ref(2, 3,): eq(1), another_field: eq(123) })
+        matches_pattern!(AStruct { *get_field_ref(2, 3,): eq(1), another_field: eq(123) })
     )
 }
 
@@ -1217,7 +1223,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { another_field: eq(234), ref get_field_ref(): eq(123) })
+        matches_pattern!(AStruct { another_field: eq(234), *get_field_ref(): eq(123) })
     )
 }
 
@@ -1240,7 +1246,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { another_field: eq(234), ref get_field_ref(): eq(123), })
+        matches_pattern!(AStruct { another_field: eq(234), *get_field_ref(): eq(123), })
     )
 }
 
@@ -1262,7 +1268,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { another_field: eq(234), ref get_field_ref(2, 3): eq(123) })
+        matches_pattern!(AStruct { another_field: eq(234), *get_field_ref(2, 3): eq(123) })
     )
 }
 
@@ -1288,7 +1294,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { another_field: eq(2), ref get_field_ref(AnEnum::AVariant): eq(1) })
+        matches_pattern!(AStruct { another_field: eq(2), *get_field_ref(AnEnum::AVariant): eq(1) })
     )
 }
 
@@ -1311,7 +1317,7 @@
 
     verify_that!(
         actual,
-        matches_pattern!(AStruct { another_field: eq(234), ref get_field_ref(2, 3,): eq(123) })
+        matches_pattern!(AStruct { another_field: eq(234), *get_field_ref(2, 3,): eq(123) })
     )
 }
 
@@ -1479,7 +1485,7 @@
         actual,
         matches_pattern!(AStruct {
             another_field: eq(234),
-            ref get_field_ref(): eq(123),
+            *get_field_ref(): eq(123),
             a_third_field: eq(345)
         })
     )
@@ -1507,7 +1513,7 @@
         actual,
         matches_pattern!(AStruct {
             another_field: eq(234),
-            ref get_field_ref(): eq(123),
+            *get_field_ref(): eq(123),
             a_third_field: eq(345),
         })
     )
@@ -1535,7 +1541,7 @@
         actual,
         matches_pattern!(AStruct {
             another_field: eq(234),
-            ref get_field_ref(2, 3): eq(123),
+            *get_field_ref(2, 3): eq(123),
             a_third_field: eq(345),
         })
     )
@@ -1567,7 +1573,7 @@
         actual,
         matches_pattern!(AStruct {
             another_field: eq(2),
-            ref get_field_ref(AnEnum::AVariant): eq(1),
+            *get_field_ref(AnEnum::AVariant): eq(1),
             a_third_field: eq(3),
         })
     )
@@ -1595,7 +1601,7 @@
         actual,
         matches_pattern!(AStruct {
             another_field: eq(234),
-            ref get_field_ref(2, 3,): eq(123),
+            *get_field_ref(2, 3,): eq(123),
             a_third_field: eq(345),
         })
     )
diff --git a/tests/pointwise_matcher_test.rs b/tests/pointwise_matcher_test.rs
index 85d16ff..cb8ef3b 100644
--- a/tests/pointwise_matcher_test.rs
+++ b/tests/pointwise_matcher_test.rs
@@ -142,8 +142,8 @@
               2. is equal to 3
             Actual: [1, 2, 3],
               where:
-              * element #0 is 1, which isn't equal to 2
-              * element #1 is 2, which isn't equal to 3"
+                * element #0 is 1, which isn't equal to 2
+                * element #1 is 2, which isn't equal to 3"
         ))))
     )
 }
diff --git a/tests/property_matcher_test.rs b/tests/property_matcher_test.rs
index f9a88be..7092446 100644
--- a/tests/property_matcher_test.rs
+++ b/tests/property_matcher_test.rs
@@ -67,7 +67,7 @@
 #[test]
 fn matches_struct_with_matching_property_ref() -> Result<()> {
     let value = SomeStruct { a_property: 10 };
-    verify_that!(value, property!(ref SomeStruct.get_property_ref(), eq(10)))
+    verify_that!(value, property!(*SomeStruct.get_property_ref(), eq(10)))
 }
 
 #[test]
@@ -82,7 +82,7 @@
         }
     }
     let value = StructWithString { property: "Something".into() };
-    verify_that!(value, property!(ref StructWithString.get_property_ref(), eq("Something")))
+    verify_that!(value, property!(*StructWithString.get_property_ref(), eq("Something")))
 }
 
 #[test]
@@ -97,19 +97,19 @@
         }
     }
     let value = StructWithVec { property: vec![1, 2, 3] };
-    verify_that!(value, property!(ref StructWithVec.get_property_ref(), eq([1, 2, 3])))
+    verify_that!(value, property!(*StructWithVec.get_property_ref(), eq([1, 2, 3])))
 }
 
 #[test]
 fn matches_struct_with_matching_property_ref_with_parameters() -> Result<()> {
     let value = SomeStruct { a_property: 10 };
-    verify_that!(value, property!(ref SomeStruct.get_property_ref_with_params(2, 3), eq(10)))
+    verify_that!(value, property!(*SomeStruct.get_property_ref_with_params(2, 3), eq(10)))
 }
 
 #[test]
 fn matches_struct_with_matching_property_ref_with_parameters_and_trailing_comma() -> Result<()> {
     let value = SomeStruct { a_property: 10 };
-    verify_that!(value, property!(ref SomeStruct.get_property_ref_with_params(2, 3,), eq(10)))
+    verify_that!(value, property!(*SomeStruct.get_property_ref_with_params(2, 3,), eq(10)))
 }
 
 #[test]
@@ -122,7 +122,7 @@
 fn describes_itself_in_matching_case() -> Result<()> {
     verify_that!(
         property!(SomeStruct.get_property(), eq(1)).describe(MatcherResult::Match),
-        eq("has property `get_property()`, which is equal to 1")
+        displays_as(eq("has property `get_property()`, which is equal to 1"))
     )
 }
 
@@ -130,7 +130,7 @@
 fn describes_itself_in_not_matching_case() -> Result<()> {
     verify_that!(
         property!(SomeStruct.get_property(), eq(1)).describe(MatcherResult::NoMatch),
-        eq("has property `get_property()`, which isn't equal to 1")
+        displays_as(eq("has property `get_property()`, which isn't equal to 1"))
     )
 }
 
@@ -155,16 +155,16 @@
 #[test]
 fn describes_itself_in_matching_case_for_ref() -> Result<()> {
     verify_that!(
-        property!(ref SomeStruct.get_property_ref(), eq(1)).describe(MatcherResult::Match),
-        eq("has property `get_property_ref()`, which is equal to 1")
+        property!(*SomeStruct.get_property_ref(), eq(1)).describe(MatcherResult::Match),
+        displays_as(eq("has property `get_property_ref()`, which is equal to 1"))
     )
 }
 
 #[test]
 fn describes_itself_in_not_matching_case_for_ref() -> Result<()> {
     verify_that!(
-        property!(ref SomeStruct.get_property_ref(), eq(1)).describe(MatcherResult::NoMatch),
-        eq("has property `get_property_ref()`, which isn't equal to 1")
+        property!(*SomeStruct.get_property_ref(), eq(1)).describe(MatcherResult::NoMatch),
+        displays_as(eq("has property `get_property_ref()`, which isn't equal to 1"))
     )
 }
 
@@ -178,7 +178,7 @@
     }
     let value = SomeStruct { a_property: 2 };
     let result =
-        verify_that!(value, property!(ref SomeStruct.get_a_collection_ref(), container_eq([1])));
+        verify_that!(value, property!(*SomeStruct.get_a_collection_ref(), container_eq([1])));
 
     verify_that!(
         result,
diff --git a/tests/tuple_matcher_test.rs b/tests/tuple_matcher_test.rs
index 0c1eb01..a93ffee 100644
--- a/tests/tuple_matcher_test.rs
+++ b/tests/tuple_matcher_test.rs
@@ -176,54 +176,50 @@
 #[test]
 fn tuple_matcher_1_has_correct_description_for_match() -> Result<()> {
     verify_that!(
-        (eq(1),).describe(MatcherResult::Match),
-        eq(indoc!(
+        (eq::<i32, _>(1),).describe(MatcherResult::Match),
+        displays_as(eq(indoc!(
             "
             is a tuple whose values respectively match:
-              is equal to 1,
-            "
-        ))
+              is equal to 1"
+        )))
     )
 }
 
 #[test]
 fn tuple_matcher_1_has_correct_description_for_mismatch() -> Result<()> {
     verify_that!(
-        (eq(1),).describe(MatcherResult::NoMatch),
-        eq(indoc!(
+        (eq::<i32, _>(1),).describe(MatcherResult::NoMatch),
+        displays_as(eq(indoc!(
             "
             is a tuple whose values do not respectively match:
-              is equal to 1,
-            "
-        ))
+              is equal to 1"
+        )))
     )
 }
 
 #[test]
 fn tuple_matcher_2_has_correct_description_for_match() -> Result<()> {
     verify_that!(
-        (eq(1), eq(2)).describe(MatcherResult::Match),
-        eq(indoc!(
+        (eq::<i32, _>(1), eq::<i32, _>(2)).describe(MatcherResult::Match),
+        displays_as(eq(indoc!(
             "
             is a tuple whose values respectively match:
-              is equal to 1,
-              is equal to 2,
-            "
-        ))
+              is equal to 1
+              is equal to 2"
+        )))
     )
 }
 
 #[test]
 fn tuple_matcher_2_has_correct_description_for_mismatch() -> Result<()> {
     verify_that!(
-        (eq(1), eq(2)).describe(MatcherResult::NoMatch),
-        eq(indoc!(
+        (eq::<i32, _>(1), eq::<i32, _>(2)).describe(MatcherResult::NoMatch),
+        displays_as(eq(indoc!(
             "
             is a tuple whose values do not respectively match:
-              is equal to 1,
-              is equal to 2,
-            "
-        ))
+              is equal to 1
+              is equal to 2"
+        )))
     )
 }
 
@@ -233,11 +229,12 @@
         (eq(1), eq(2)).explain_match(&(1, 3)),
         displays_as(eq(indoc!(
             "
-            which is a tuple whose values do not respectively match:
-              is equal to 1,
-              is equal to 2,
-            Element #1 is 3, which isn't equal to 2
-            "
+            which
+              is a tuple whose values do not respectively match:
+                is equal to 1
+                is equal to 2
+            Element #1 is 3,
+              which isn't equal to 2"
         )))
     )
 }
@@ -248,12 +245,14 @@
         (eq(1), eq(2)).explain_match(&(2, 3)),
         displays_as(eq(indoc!(
             "
-            which is a tuple whose values do not respectively match:
-              is equal to 1,
-              is equal to 2,
-            Element #0 is 2, which isn't equal to 1
-            Element #1 is 3, which isn't equal to 2
-            "
+            which
+              is a tuple whose values do not respectively match:
+                is equal to 1
+                is equal to 2
+            Element #0 is 2,
+              which isn't equal to 1
+            Element #1 is 3,
+              which isn't equal to 2"
         )))
     )
 }
diff --git a/tests/unordered_elements_are_matcher_test.rs b/tests/unordered_elements_are_matcher_test.rs
index a105b70..bd61417 100644
--- a/tests/unordered_elements_are_matcher_test.rs
+++ b/tests/unordered_elements_are_matcher_test.rs
@@ -352,7 +352,7 @@
 
 #[test]
 fn is_contained_in_matches_when_container_is_empty() -> Result<()> {
-    verify_that!(vec![], is_contained_in!(eq(2), eq(3), eq(4)))
+    verify_that!(vec![], is_contained_in!(eq::<i32, _>(2), eq(3), eq(4)))
 }
 
 #[test]