Repository: rust-lang/regex
Pull request #726 (2020-11-24 21:26:42)

Use HTTPS for links


Patch:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..ef6cf89
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,34 @@
+---
+name: Bug report
+about: An issue with the current behavior of the regex crate.
+title: ''
+labels: ''
+assignees: ''
+---
+
+
+#### What version of regex are you using?
+
+If it isn't the latest version, then please upgrade and check whether the bug
+is still present.
+
+#### Describe the bug at a high level.
+
+Give a brief description of the actual problem you're observing.
+
+#### What are the steps to reproduce the behavior?
+
+This section should almost always provide a COMPLETE Rust program that others
+can compile and run with Cargo. It should be as small as possible.
+
+If providing a small and simple reproduction is not easy, please explain why
+and the maintainers will help you figure out next steps.
+
+#### What is the actual behavior?
+
+If you provide a Rust program in the previous section, then this should be the
+output of that program.
+
+#### What is the expected behavior?
+
+What do you expect the output to be?
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..99fab81
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,7 @@
+blank_issues_enabled: true
+contact_links:
+  - name: Ask a question
+    about: |
+      You've come to seek help or want to discuss something related to the
+      regex crate.
+    url: https://github.com/rust-lang/regex/discussions/new
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..a308352
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest a new feature
+title: ''
+labels: ''
+assignees: ''
+---
+
+
+#### Describe your feature request
+
+Please describe the behavior you want and the motivation. Please also provide
+examples of how the new feature would be used if your feature request were
+added.
+
+The best kind of feature request is one that proposes a concrete change to the
+API along with a sketch of the changes to the public API documentation.
+
+If you're not sure what to write here, then try imagining what the ideal
+documentation of your new feature would look like. Then try to write it.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9dfe497..cf020d2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,16 @@
+1.4.3 (2021-01-08)
+==================
+This is a small patch release that adds some missing standard trait
+implementations for some types in the public API.
+
+Bug fixes:
+
+* [BUG #734](https://github.com/rust-lang/regex/pull/734):
+  Add `FusedIterator` and `ExactSizeIterator` impls to iterator types.
+* [BUG #735](https://github.com/rust-lang/regex/pull/735):
+  Add missing `Debug` impls to public API types.
+
+
 1.4.2 (2020-11-01)
 ==================
 This is a small bug fix release that bans `\P{any}`. We previously banned empty
diff --git a/Cargo.toml b/Cargo.toml
index dac60bb..5e1545f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "regex"
-version = "1.4.2"  #:version
+version = "1.4.3"  #:version
 authors = ["The Rust Project Developers"]
 license = "MIT OR Apache-2.0"
 readme = "README.md"
@@ -118,7 +118,7 @@ optional = true
 # For parsing regular expressions.
 [dependencies.regex-syntax]
 path = "regex-syntax"
-version = "0.6.21"
+version = "0.6.22"
 default-features = false
 
 [dev-dependencies]
diff --git a/README.md b/README.md
index 8c05a90..f7a2554 100644
--- a/README.md
+++ b/README.md
@@ -245,12 +245,12 @@ supported version of Rust.
 This project is licensed under either of
 
  * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
-   http://www.apache.org/licenses/LICENSE-2.0)
+   https://www.apache.org/licenses/LICENSE-2.0)
  * MIT license ([LICENSE-MIT](LICENSE-MIT) or
-   http://opensource.org/licenses/MIT)
+   https://opensource.org/licenses/MIT)
 
 at your option.
 
 The data in `regex-syntax/src/unicode_tables/` is licensed under the Unicode
 License Agreement
-([LICENSE-UNICODE](http://www.unicode.org/copyright.html#License)).
+([LICENSE-UNICODE](https://www.unicode.org/copyright.html#License)).
diff --git a/UNICODE.md b/UNICODE.md
index 18fa9b1..df7d21e 100644
--- a/UNICODE.md
+++ b/UNICODE.md
@@ -1,7 +1,7 @@
 # Unicode conformance
 
 This document describes the regex crate's conformance to Unicode's
-[UTS#18](http://unicode.org/reports/tr18/)
+[UTS#18](https://unicode.org/reports/tr18/)
 report, which lays out 3 levels of support: Basic, Extended and Tailored.
 
 Full support for Level 1 ("Basic Unicode Support") is provided with two
@@ -10,7 +10,7 @@ exceptions:
 1. Line boundaries are not Unicode aware. Namely, only the `\n`
    (`END OF LINE`) character is recognized as a line boundary.
 2. The compatibility properties specified by
-   [RL1.2a](http://unicode.org/reports/tr18/#RL1.2a)
+   [RL1.2a](https://unicode.org/reports/tr18/#RL1.2a)
    are ASCII-only definitions.
 
 Little to no support is provided for either Level 2 or Level 3. For the most
@@ -61,18 +61,18 @@ provide a convenient way to construct character classes of groups of code
 points specified by Unicode. The regex crate does not provide exhaustive
 support, but covers a useful subset. In particular:
 
-* [General categories](http://unicode.org/reports/tr18/#General_Category_Property)
-* [Scripts and Script Extensions](http://unicode.org/reports/tr18/#Script_Property)
-* [Age](http://unicode.org/reports/tr18/#Age)
+* [General categories](https://unicode.org/reports/tr18/#General_Category_Property)
+* [Scripts and Script Extensions](https://unicode.org/reports/tr18/#Script_Property)
+* [Age](https://unicode.org/reports/tr18/#Age)
 * A smattering of boolean properties, including all of those specified by
-  [RL1.2](http://unicode.org/reports/tr18/#RL1.2) explicitly.
+  [RL1.2](https://unicode.org/reports/tr18/#RL1.2) explicitly.
 
 In all cases, property name and value abbreviations are supported, and all
 names/values are matched loosely without regard for case, whitespace or
 underscores. Property name aliases can be found in Unicode's
-[`PropertyAliases.txt`](http://www.unicode.org/Public/UCD/latest/ucd/PropertyAliases.txt)
+[`PropertyAliases.txt`](https://www.unicode.org/Public/UCD/latest/ucd/PropertyAliases.txt)
 file, while property value aliases can be found in Unicode's
-[`PropertyValueAliases.txt`](http://www.unicode.org/Public/UCD/latest/ucd/PropertyValueAliases.txt)
+[`PropertyValueAliases.txt`](https://www.unicode.org/Public/UCD/latest/ucd/PropertyValueAliases.txt)
 file.
 
 The syntax supported is also consistent with the UTS#18 recommendation:
@@ -149,10 +149,10 @@ properties correspond to properties required by RL1.2):
 
 ## RL1.2a Compatibility Properties
 
-[UTS#18 RL1.2a](http://unicode.org/reports/tr18/#RL1.2a)
+[UTS#18 RL1.2a](https://unicode.org/reports/tr18/#RL1.2a)
 
 The regex crate only provides ASCII definitions of the
-[compatibility properties documented in UTS#18 Annex C](http://unicode.org/reports/tr18/#Compatibility_Properties)
+[compatibility properties documented in UTS#18 Annex C](https://unicode.org/reports/tr18/#Compatibility_Properties)
 (sans the `\X` class, for matching grapheme clusters, which isn't provided
 at all). This is because it seems to be consistent with most other regular
 expression engines, and in particular, because these are often referred to as
@@ -165,7 +165,7 @@ Their traditional ASCII definition can be used by disabling Unicode. That is,
 
 ## RL1.3 Subtraction and Intersection
 
-[UTS#18 RL1.3](http://unicode.org/reports/tr18/#Subtraction_and_Intersection)
+[UTS#18 RL1.3](https://unicode.org/reports/tr18/#Subtraction_and_Intersection)
 
 The regex crate provides full support for nested character classes, along with
 union, intersection (`&&`), difference (`--`) and symmetric difference (`~~`)
@@ -178,7 +178,7 @@ For example, to match all non-ASCII letters, you could use either
 
 ## RL1.4 Simple Word Boundaries
 
-[UTS#18 RL1.4](http://unicode.org/reports/tr18/#Simple_Word_Boundaries)
+[UTS#18 RL1.4](https://unicode.org/reports/tr18/#Simple_Word_Boundaries)
 
 The regex crate provides basic Unicode aware word boundary assertions. A word
 boundary assertion can be written as `\b`, or `\B` as its negation. A word
@@ -196,9 +196,9 @@ the following classes:
 * `\p{gc:Connector_Punctuation}`
 
 In particular, this differs slightly from the
-[prescription given in RL1.4](http://unicode.org/reports/tr18/#Simple_Word_Boundaries)
+[prescription given in RL1.4](https://unicode.org/reports/tr18/#Simple_Word_Boundaries)
 but is permissible according to
-[UTS#18 Annex C](http://unicode.org/reports/tr18/#Compatibility_Properties).
+[UTS#18 Annex C](https://unicode.org/reports/tr18/#Compatibility_Properties).
 Namely, it is convenient and simpler to have `\w` and `\b` be in sync with
 one another.
 
@@ -211,7 +211,7 @@ boundaries is currently sub-optimal on non-ASCII text.
 
 ## RL1.5 Simple Loose Matches
 
-[UTS#18 RL1.5](http://unicode.org/reports/tr18/#Simple_Loose_Matches)
+[UTS#18 RL1.5](https://unicode.org/reports/tr18/#Simple_Loose_Matches)
 
 The regex crate provides full support for case insensitive matching in
 accordance with RL1.5. That is, it uses the "simple" case folding mapping. The
@@ -226,7 +226,7 @@ then all characters classes are case folded as well.
 
 ## RL1.6 Line Boundaries
 
-[UTS#18 RL1.6](http://unicode.org/reports/tr18/#Line_Boundaries)
+[UTS#18 RL1.6](https://unicode.org/reports/tr18/#Line_Boundaries)
 
 The regex crate only provides support for recognizing the `\n` (`END OF LINE`)
 character as a line boundary. This choice was made mostly for implementation
@@ -239,7 +239,7 @@ well, and in theory, this could be done efficiently.
 
 ## RL1.7 Code Points
 
-[UTS#18 RL1.7](http://unicode.org/reports/tr18/#Supplementary_Characters)
+[UTS#18 RL1.7](https://unicode.org/reports/tr18/#Supplementary_Characters)
 
 The regex crate provides full support for Unicode code point matching. Namely,
 the fundamental atom of any match is always a single code point.
diff --git a/regex-syntax/Cargo.toml b/regex-syntax/Cargo.toml
index 7b56f32..765eb37 100644
--- a/regex-syntax/Cargo.toml
+++ b/regex-syntax/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "regex-syntax"
-version = "0.6.21"  #:version
+version = "0.6.22"  #:version
 authors = ["The Rust Project Developers"]
 license = "MIT/Apache-2.0"
 repository = "https://github.com/rust-lang/regex"
diff --git a/regex-syntax/src/lib.rs b/regex-syntax/src/lib.rs
index f35c913..6be634a 100644
--- a/regex-syntax/src/lib.rs
+++ b/regex-syntax/src/lib.rs
@@ -155,6 +155,7 @@ The following features are available:
 */
 
 #![deny(missing_docs)]
+#![warn(missing_debug_implementations)]
 #![forbid(unsafe_code)]
 
 pub use error::{Error, Result};
@@ -215,7 +216,7 @@ pub fn is_meta_character(c: char) -> bool {
 /// character.
 ///
 /// A Unicode word character is defined by
-/// [UTS#18 Annex C](http://unicode.org/reports/tr18/#Compatibility_Properties).
+/// [UTS#18 Annex C](https://unicode.org/reports/tr18/#Compatibility_Properties).
 /// In particular, a character
 /// is considered a word character if it is in either of the `Alphabetic` or
 /// `Join_Control` properties, or is in one of the `Decimal_Number`, `Mark`
@@ -235,7 +236,7 @@ pub fn is_word_character(c: char) -> bool {
 /// character.
 ///
 /// A Unicode word character is defined by
-/// [UTS#18 Annex C](http://unicode.org/reports/tr18/#Compatibility_Properties).
+/// [UTS#18 Annex C](https://unicode.org/reports/tr18/#Compatibility_Properties).
 /// In particular, a character
 /// is considered a word character if it is in either of the `Alphabetic` or
 /// `Join_Control` properties, or is in one of the `Decimal_Number`, `Mark`
diff --git a/regex-syntax/src/unicode.rs b/regex-syntax/src/unicode.rs
index a78362b..73df698 100644
--- a/regex-syntax/src/unicode.rs
+++ b/regex-syntax/src/unicode.rs
@@ -823,7 +823,7 @@ fn symbolic_name_normalize(x: &str) -> String {
 /// The slice returned is guaranteed to be valid UTF-8 for all possible values
 /// of `slice`.
 ///
-/// See: http://unicode.org/reports/tr44/#UAX44-LM3
+/// See: https://unicode.org/reports/tr44/#UAX44-LM3
 fn symbolic_name_normalize_bytes(slice: &mut [u8]) -> &mut [u8] {
     // I couldn't find a place in the standard that specified that property
     // names/aliases had a particular structure (unlike character names), but
diff --git a/regex-syntax/src/utf8.rs b/regex-syntax/src/utf8.rs
index 489729e..89862af 100644
--- a/regex-syntax/src/utf8.rs
+++ b/regex-syntax/src/utf8.rs
@@ -84,6 +84,7 @@ which uses it for executing automata on their term index.
 
 use std::char;
 use std::fmt;
+use std::iter::FusedIterator;
 use std::slice;
 
 const MAX_UTF8_BYTES: usize = 4;
@@ -295,6 +296,7 @@ impl fmt::Debug for Utf8Range {
 /// illustrative. In practice, you could just try to decode your byte sequence
 /// and compare it with the scalar value range directly. However, this is not
 /// always possible (for example, in a byte based automaton).
+#[derive(Debug)]
 pub struct Utf8Sequences {
     range_stack: Vec<ScalarRange>,
 }
@@ -388,6 +390,8 @@ impl Iterator for Utf8Sequences {
     }
 }
 
+impl FusedIterator for Utf8Sequences {}
+
 impl ScalarRange {
     /// split splits this range if it overlaps with a surrogate codepoint.
     ///
diff --git a/src/compile.rs b/src/compile.rs
index cdc583c..9ffd347 100644
--- a/src/compile.rs
+++ b/src/compile.rs
@@ -1,4 +1,5 @@
 use std::collections::HashMap;
+use std::fmt;
 use std::iter;
 use std::result;
 use std::sync::Arc;
@@ -25,6 +26,9 @@ struct Patch {
 
 /// A compiler translates a regular expression AST to a sequence of
 /// instructions. The sequence of instructions represents an NFA.
+// `Compiler` is only public via the `internal` module, so avoid deriving
+// `Debug`.
+#[allow(missing_debug_implementations)]
 pub struct Compiler {
     insts: Vec<MaybeInst>,
     compiled: Program,
@@ -1051,6 +1055,7 @@ impl<'a, 'b> CompileClass<'a, 'b> {
 /// This uses similar idea to [`SparseSet`](../sparse/struct.SparseSet.html),
 /// except it uses hashes as original indices and then compares full keys for
 /// validation against `dense` array.
+#[derive(Debug)]
 struct SuffixCache {
     sparse: Box<[usize]>,
     dense: Vec<SuffixCacheEntry>,
@@ -1159,6 +1164,12 @@ impl ByteClassSet {
     }
 }
 
+impl fmt::Debug for ByteClassSet {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_tuple("ByteClassSet").field(&&self.0[..]).finish()
+    }
+}
+
 fn u32_to_usize(n: u32) -> usize {
     // In case usize is less than 32 bits, we need to guard against overflow.
     // On most platforms this compiles to nothing.
diff --git a/src/exec.rs b/src/exec.rs
index acca2dc..e1aae87 100644
--- a/src/exec.rs
+++ b/src/exec.rs
@@ -30,6 +30,7 @@ use utf8::next_utf8;
 /// In particular, this manages the various compiled forms of a single regular
 /// expression and the choice of which matching engine to use to execute a
 /// regular expression.
+#[derive(Debug)]
 pub struct Exec {
     /// All read only state.
     ro: Arc<ExecReadOnly>,
@@ -49,6 +50,7 @@ pub struct ExecNoSync<'c> {
 }
 
 /// `ExecNoSyncStr` is like `ExecNoSync`, but matches on &str instead of &[u8].
+#[derive(Debug)]
 pub struct ExecNoSyncStr<'c>(ExecNoSync<'c>);
 
 /// `ExecReadOnly` comprises all read only state for a regex. Namely, all such
@@ -97,6 +99,9 @@ struct ExecReadOnly {
 /// Facilitates the construction of an executor by exposing various knobs
 /// to control how a regex is executed and what kinds of resources it's
 /// permitted to use.
+// `ExecBuilder` is only public via the `internal` module, so avoid deriving
+// `Debug`.
+#[allow(missing_debug_implementations)]
 pub struct ExecBuilder {
     options: RegexOptions,
     match_type: Option<MatchType>,
diff --git a/src/lib.rs b/src/lib.rs
index bdcebd4..c9fe74f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -253,7 +253,7 @@ assert_eq!((mat.start(), mat.end()), (3, 23));
 ```
 
 For a more detailed breakdown of Unicode support with respect to
-[UTS#18](http://unicode.org/reports/tr18/),
+[UTS#18](https://unicode.org/reports/tr18/),
 please see the
 [UNICODE](https://github.com/rust-lang/regex/blob/master/UNICODE.md)
 document in the root of the regex repository.
@@ -455,7 +455,7 @@ assert_eq!(&cap[0], "abc");
 ## Perl character classes (Unicode friendly)
 
 These classes are based on the definitions provided in
-[UTS#18](http://www.unicode.org/reports/tr18/#Compatibility_Properties):
+[UTS#18](https://www.unicode.org/reports/tr18/#Compatibility_Properties):
 
 <pre class="rust">
 \d     digit (\p{Nd})
@@ -616,6 +616,7 @@ another matching engine with fixed memory requirements.
 #![deny(missing_docs)]
 #![cfg_attr(test, deny(warnings))]
 #![cfg_attr(feature = "pattern", feature(pattern))]
+#![warn(missing_debug_implementations)]
 
 #[cfg(not(feature = "std"))]
 compile_error!("`std` feature is currently required to build this crate");
diff --git a/src/literal/imp.rs b/src/literal/imp.rs
index fe07ffc..e4d04ed 100644
--- a/src/literal/imp.rs
+++ b/src/literal/imp.rs
@@ -72,7 +72,7 @@ impl LiteralSearcher {
     /// Returns true if all matches comprise the entire regular expression.
     ///
     /// This does not necessarily mean that a literal match implies a match
-    /// of the regular expression. For example, the regular expresison `^a`
+    /// of the regular expression. For example, the regular expression `^a`
     /// is comprised of a single complete literal `a`, but the regular
     /// expression demands that it only match at the beginning of a string.
     pub fn complete(&self) -> bool {
@@ -232,6 +232,7 @@ impl Matcher {
     }
 }
 
+#[derive(Debug)]
 pub enum LiteralIter<'a> {
     Empty,
     Bytes(&'a [u8]),
diff --git a/src/re_builder.rs b/src/re_builder.rs
index 3fef99d..fc140f8 100644
--- a/src/re_builder.rs
+++ b/src/re_builder.rs
@@ -47,6 +47,7 @@ macro_rules! define_builder {
             /// A builder can be used to configure how the regex is built, for example, by
             /// setting the default flags (which can be overridden in the expression
             /// itself) or setting various limits.
+            #[derive(Debug)]
             pub struct RegexBuilder(RegexOptions);
 
             impl RegexBuilder {
@@ -244,6 +245,7 @@ macro_rules! define_set_builder {
             /// A builder can be used to configure how the regexes are built, for example,
             /// by setting the default flags (which can be overridden in the expression
             /// itself) or setting various limits.
+            #[derive(Debug)]
             pub struct RegexSetBuilder(RegexOptions);
 
             impl RegexSetBuilder {
diff --git a/src/re_bytes.rs b/src/re_bytes.rs
index ca01e0e..204a70a 100644
--- a/src/re_bytes.rs
+++ b/src/re_bytes.rs
@@ -1,6 +1,7 @@
 use std::borrow::Cow;
 use std::collections::HashMap;
 use std::fmt;
+use std::iter::FusedIterator;
 use std::ops::{Index, Range};
 use std::str::FromStr;
 use std::sync::Arc;
@@ -690,6 +691,7 @@ impl Regex {
 ///
 /// `'r` is the lifetime of the compiled regular expression and `'t` is the
 /// lifetime of the matched byte string.
+#[derive(Debug)]
 pub struct Matches<'r, 't>(re_trait::Matches<'t, ExecNoSync<'r>>);
 
 impl<'r, 't> Iterator for Matches<'r, 't> {
@@ -701,6 +703,8 @@ impl<'r, 't> Iterator for Matches<'r, 't> {
     }
 }
 
+impl<'r, 't> FusedIterator for Matches<'r, 't> {}
+
 /// An iterator that yields all non-overlapping capture groups matching a
 /// particular regular expression.
 ///
@@ -708,6 +712,7 @@ impl<'r, 't> Iterator for Matches<'r, 't> {
 ///
 /// `'r` is the lifetime of the compiled regular expression and `'t` is the
 /// lifetime of the matched byte string.
+#[derive(Debug)]
 pub struct CaptureMatches<'r, 't>(
     re_trait::CaptureMatches<'t, ExecNoSync<'r>>,
 );
@@ -724,10 +729,13 @@ impl<'r, 't> Iterator for CaptureMatches<'r, 't> {
     }
 }
 
+impl<'r, 't> FusedIterator for CaptureMatches<'r, 't> {}
+
 /// Yields all substrings delimited by a regular expression match.
 ///
 /// `'r` is the lifetime of the compiled regular expression and `'t` is the
 /// lifetime of the byte string being split.
+#[derive(Debug)]
 pub struct Split<'r, 't> {
     finder: Matches<'r, 't>,
     last: usize,
@@ -757,12 +765,15 @@ impl<'r, 't> Iterator for Split<'r, 't> {
     }
 }
 
+impl<'r, 't> FusedIterator for Split<'r, 't> {}
+
 /// Yields at most `N` substrings delimited by a regular expression match.
 ///
 /// The last substring will be whatever remains after splitting.
 ///
 /// `'r` is the lifetime of the compiled regular expression and `'t` is the
 /// lifetime of the byte string being split.
+#[derive(Debug)]
 pub struct SplitN<'r, 't> {
     splits: Split<'r, 't>,
     n: usize,
@@ -790,14 +801,21 @@ impl<'r, 't> Iterator for SplitN<'r, 't> {
             Some(&text[self.splits.last..])
         }
     }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, Some(self.n))
+    }
 }
 
+impl<'r, 't> FusedIterator for SplitN<'r, 't> {}
+
 /// An iterator over the names of all possible captures.
 ///
 /// `None` indicates an unnamed capture; the first element (capture 0, the
 /// whole matched region) is always unnamed.
 ///
 /// `'r` is the lifetime of the compiled regular expression.
+#[derive(Clone, Debug)]
 pub struct CaptureNames<'r>(::std::slice::Iter<'r, Option<String>>);
 
 impl<'r> Iterator for CaptureNames<'r> {
@@ -813,8 +831,16 @@ impl<'r> Iterator for CaptureNames<'r> {
     fn size_hint(&self) -> (usize, Option<usize>) {
         self.0.size_hint()
     }
+
+    fn count(self) -> usize {
+        self.0.count()
+    }
 }
 
+impl<'r> ExactSizeIterator for CaptureNames<'r> {}
+
+impl<'r> FusedIterator for CaptureNames<'r> {}
+
 /// CaptureLocations is a low level representation of the raw offsets of each
 /// submatch.
 ///
@@ -1057,7 +1083,7 @@ impl<'t, 'i> Index<&'i str> for Captures<'t> {
 ///
 /// The lifetime `'c` corresponds to the lifetime of the `Captures` value, and
 /// the lifetime `'t` corresponds to the originally matched text.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct SubCaptureMatches<'c, 't: 'c> {
     caps: &'c Captures<'t>,
     it: SubCapturesPosIter<'c>,
@@ -1073,13 +1099,15 @@ impl<'c, 't> Iterator for SubCaptureMatches<'c, 't> {
     }
 }
 
+impl<'c, 't> FusedIterator for SubCaptureMatches<'c, 't> {}
+
 /// Replacer describes types that can be used to replace matches in a byte
 /// string.
 ///
 /// In general, users of this crate shouldn't need to implement this trait,
-/// since implementations are already provided for `&[u8]` and
-/// `FnMut(&Captures) -> Vec<u8>` (or any `FnMut(&Captures) -> T`
-/// where `T: AsRef<[u8]>`), which covers most use cases.
+/// since implementations are already provided for `&[u8]` along with other
+/// variants of bytes types and `FnMut(&Captures) -> Vec<u8>` (or any
+/// `FnMut(&Captures) -> T` where `T: AsRef<[u8]>`), which covers most use cases.
 pub trait Replacer {
     /// Appends text to `dst` to replace the current match.
     ///
@@ -1148,10 +1176,55 @@ impl<'a> Replacer for &'a [u8] {
     }
 
     fn no_expansion(&mut self) -> Option<Cow<[u8]>> {
-        match find_byte(b'$', *self) {
-            Some(_) => None,
-            None => Some(Cow::Borrowed(*self)),
-        }
+        no_expansion(self)
+    }
+}
+
+impl<'a> Replacer for &'a Vec<u8> {
+    fn replace_append(&mut self, caps: &Captures, dst: &mut Vec<u8>) {
+        caps.expand(*self, dst);
+    }
+
+    fn no_expansion(&mut self) -> Option<Cow<[u8]>> {
+        no_expansion(self)
+    }
+}
+
+impl Replacer for Vec<u8> {
+    fn replace_append(&mut self, caps: &Captures, dst: &mut Vec<u8>) {
+        caps.expand(self, dst);
+    }
+
+    fn no_expansion(&mut self) -> Option<Cow<[u8]>> {
+        no_expansion(self)
+    }
+}
+
+impl<'a> Replacer for Cow<'a, [u8]> {
+    fn replace_append(&mut self, caps: &Captures, dst: &mut Vec<u8>) {
+        caps.expand(self.as_ref(), dst);
+    }
+
+    fn no_expansion(&mut self) -> Option<Cow<[u8]>> {
+        no_expansion(self)
+    }
+}
+
+impl<'a> Replacer for &'a Cow<'a, [u8]> {
+    fn replace_append(&mut self, caps: &Captures, dst: &mut Vec<u8>) {
+        caps.expand(self.as_ref(), dst);
+    }
+
+    fn no_expansion(&mut self) -> Option<Cow<[u8]>> {
+        no_expansion(self)
+    }
+}
+
+fn no_expansion<T: AsRef<[u8]>>(t: &T) -> Option<Cow<[u8]>> {
+    let s = t.as_ref();
+    match find_byte(b'$', s) {
+        Some(_) => None,
+        None => Some(Cow::Borrowed(s)),
     }
 }
 
@@ -1173,6 +1246,7 @@ where
 /// and performant (since capture groups don't need to be found).
 ///
 /// `'t` is the lifetime of the literal text.
+#[derive(Clone, Debug)]
 pub struct NoExpand<'t>(pub &'t [u8]);
 
 impl<'t> Replacer for NoExpand<'t> {
diff --git a/src/re_set.rs b/src/re_set.rs
index b8954be..0a00229 100644
--- a/src/re_set.rs
+++ b/src/re_set.rs
@@ -320,6 +320,7 @@ impl<'a> IntoIterator for &'a SetMatches {
 /// This will always produces matches in ascending order of index, where the
 /// index corresponds to the index of the regex that matched with respect to
 /// its position when initially building the set.
+#[derive(Debug)]
 pub struct SetMatchesIntoIter(iter::Enumerate<vec::IntoIter<bool>>);
 
 impl Iterator for SetMatchesIntoIter {
@@ -352,6 +353,8 @@ impl DoubleEndedIterator for SetMatchesIntoIter {
     }
 }
 
+impl iter::FusedIterator for SetMatchesIntoIter {}
+
 /// A borrowed iterator over the set of matches from a regex set.
 ///
 /// The lifetime `'a` refers to the lifetime of a `SetMatches` value.
@@ -359,7 +362,7 @@ impl DoubleEndedIterator for SetMatchesIntoIter {
 /// This will always produces matches in ascending order of index, where the
 /// index corresponds to the index of the regex that matched with respect to
 /// its position when initially building the set.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct SetMatchesIter<'a>(iter::Enumerate<slice::Iter<'a, bool>>);
 
 impl<'a> Iterator for SetMatchesIter<'a> {
@@ -392,6 +395,8 @@ impl<'a> DoubleEndedIterator for SetMatchesIter<'a> {
     }
 }
 
+impl<'a> iter::FusedIterator for SetMatchesIter<'a> {}
+
 #[doc(hidden)]
 impl From<Exec> for RegexSet {
     fn from(exec: Exec) -> Self {
diff --git a/src/re_trait.rs b/src/re_trait.rs
index d14a9f7..ea6be9c 100644
--- a/src/re_trait.rs
+++ b/src/re_trait.rs
@@ -1,3 +1,6 @@
+use std::fmt;
+use std::iter::FusedIterator;
+
 /// Slot is a single saved capture location. Note that there are two slots for
 /// every capture in a regular expression (one slot each for the start and end
 /// of the capture).
@@ -51,7 +54,7 @@ impl Locations {
 /// Positions are byte indices in terms of the original string matched.
 ///
 /// `'c` is the lifetime of the captures.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct SubCapturesPosIter<'c> {
     idx: usize,
     locs: &'c Locations,
@@ -73,6 +76,8 @@ impl<'c> Iterator for SubCapturesPosIter<'c> {
     }
 }
 
+impl<'c> FusedIterator for SubCapturesPosIter<'c> {}
+
 /// `RegularExpression` describes types that can implement regex searching.
 ///
 /// This trait is my attempt at reducing code duplication and to standardize
@@ -85,9 +90,9 @@ impl<'c> Iterator for SubCapturesPosIter<'c> {
 /// somewhat reasonable. One particular thing this trait would expose would be
 /// the ability to start the search of a regex anywhere in a haystack, which
 /// isn't possible in the current public API.
-pub trait RegularExpression: Sized {
+pub trait RegularExpression: Sized + fmt::Debug {
     /// The type of the haystack.
-    type Text: ?Sized;
+    type Text: ?Sized + fmt::Debug;
 
     /// The number of capture slots in the compiled regular expression. This is
     /// always two times the number of capture groups (two slots per group).
@@ -145,6 +150,7 @@ pub trait RegularExpression: Sized {
 }
 
 /// An iterator over all non-overlapping successive leftmost-first matches.
+#[derive(Debug)]
 pub struct Matches<'t, R>
 where
     R: RegularExpression,
@@ -205,8 +211,16 @@ where
     }
 }
 
+impl<'t, R> FusedIterator for Matches<'t, R>
+where
+    R: RegularExpression,
+    R::Text: 't + AsRef<[u8]>,
+{
+}
+
 /// An iterator over all non-overlapping successive leftmost-first matches with
 /// captures.
+#[derive(Debug)]
 pub struct CaptureMatches<'t, R>(Matches<'t, R>)
 where
     R: RegularExpression,
@@ -260,3 +274,10 @@ where
         Some(locs)
     }
 }
+
+impl<'t, R> FusedIterator for CaptureMatches<'t, R>
+where
+    R: RegularExpression,
+    R::Text: 't + AsRef<[u8]>,
+{
+}
diff --git a/src/re_unicode.rs b/src/re_unicode.rs
index ea95c1b..1b478cd 100644
--- a/src/re_unicode.rs
+++ b/src/re_unicode.rs
@@ -1,6 +1,7 @@
 use std::borrow::Cow;
 use std::collections::HashMap;
 use std::fmt;
+use std::iter::FusedIterator;
 use std::ops::{Index, Range};
 use std::str::FromStr;
 use std::sync::Arc;
@@ -747,6 +748,7 @@ impl Regex {
 /// whole matched region) is always unnamed.
 ///
 /// `'r` is the lifetime of the compiled regular expression.
+#[derive(Clone, Debug)]
 pub struct CaptureNames<'r>(::std::slice::Iter<'r, Option<String>>);
 
 impl<'r> Iterator for CaptureNames<'r> {
@@ -762,12 +764,21 @@ impl<'r> Iterator for CaptureNames<'r> {
     fn size_hint(&self) -> (usize, Option<usize>) {
         self.0.size_hint()
     }
+
+    fn count(self) -> usize {
+        self.0.count()
+    }
 }
 
+impl<'r> ExactSizeIterator for CaptureNames<'r> {}
+
+impl<'r> FusedIterator for CaptureNames<'r> {}
+
 /// Yields all substrings delimited by a regular expression match.
 ///
 /// `'r` is the lifetime of the compiled regular expression and `'t` is the
 /// lifetime of the string being split.
+#[derive(Debug)]
 pub struct Split<'r, 't> {
     finder: Matches<'r, 't>,
     last: usize,
@@ -797,12 +808,15 @@ impl<'r, 't> Iterator for Split<'r, 't> {
     }
 }
 
+impl<'r, 't> FusedIterator for Split<'r, 't> {}
+
 /// Yields at most `N` substrings delimited by a regular expression match.
 ///
 /// The last substring will be whatever remains after splitting.
 ///
 /// `'r` is the lifetime of the compiled regular expression and `'t` is the
 /// lifetime of the string being split.
+#[derive(Debug)]
 pub struct SplitN<'r, 't> {
     splits: Split<'r, 't>,
     n: usize,
@@ -830,8 +844,14 @@ impl<'r, 't> Iterator for SplitN<'r, 't> {
             Some(&text[self.splits.last..])
         }
     }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, Some(self.n))
+    }
 }
 
+impl<'r, 't> FusedIterator for SplitN<'r, 't> {}
+
 /// CaptureLocations is a low level representation of the raw offsets of each
 /// submatch.
 ///
@@ -1059,7 +1079,7 @@ impl<'t, 'i> Index<&'i str> for Captures<'t> {
 ///
 /// The lifetime `'c` corresponds to the lifetime of the `Captures` value, and
 /// the lifetime `'t` corresponds to the originally matched text.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct SubCaptureMatches<'c, 't: 'c> {
     caps: &'c Captures<'t>,
     it: SubCapturesPosIter<'c>,
@@ -1075,6 +1095,8 @@ impl<'c, 't> Iterator for SubCaptureMatches<'c, 't> {
     }
 }
 
+impl<'c, 't> FusedIterator for SubCaptureMatches<'c, 't> {}
+
 /// An iterator that yields all non-overlapping capture groups matching a
 /// particular regular expression.
 ///
@@ -1082,6 +1104,7 @@ impl<'c, 't> Iterator for SubCaptureMatches<'c, 't> {
 ///
 /// `'r` is the lifetime of the compiled regular expression and `'t` is the
 /// lifetime of the matched string.
+#[derive(Debug)]
 pub struct CaptureMatches<'r, 't>(
     re_trait::CaptureMatches<'t, ExecNoSyncStr<'r>>,
 );
@@ -1098,6 +1121,8 @@ impl<'r, 't> Iterator for CaptureMatches<'r, 't> {
     }
 }
 
+impl<'r, 't> FusedIterator for CaptureMatches<'r, 't> {}
+
 /// An iterator over all non-overlapping matches for a particular string.
 ///
 /// The iterator yields a `Match` value. The iterator stops when no more
@@ -1105,6 +1130,7 @@ impl<'r, 't> Iterator for CaptureMatches<'r, 't> {
 ///
 /// `'r` is the lifetime of the compiled regular expression and `'t` is the
 /// lifetime of the matched string.
+#[derive(Debug)]
 pub struct Matches<'r, 't>(re_trait::Matches<'t, ExecNoSyncStr<'r>>);
 
 impl<'r, 't> Iterator for Matches<'r, 't> {
@@ -1116,12 +1142,14 @@ impl<'r, 't> Iterator for Matches<'r, 't> {
     }
 }
 
+impl<'r, 't> FusedIterator for Matches<'r, 't> {}
+
 /// Replacer describes types that can be used to replace matches in a string.
 ///
 /// In general, users of this crate shouldn't need to implement this trait,
-/// since implementations are already provided for `&str` and
-/// `FnMut(&Captures) -> String` (or any `FnMut(&Captures) -> T`
-/// where `T: AsRef<str>`), which covers most use cases.
+/// since implementations are already provided for `&str` along with other
+/// variants of string types and `FnMut(&Captures) -> String` (or any
+/// `FnMut(&Captures) -> T` where `T: AsRef<str>`), which covers most use cases.
 pub trait Replacer {
     /// Appends text to `dst` to replace the current match.
     ///
@@ -1190,10 +1218,55 @@ impl<'a> Replacer for &'a str {
     }
 
     fn no_expansion(&mut self) -> Option<Cow<str>> {
-        match find_byte(b'$', self.as_bytes()) {
-            Some(_) => None,
-            None => Some(Cow::Borrowed(*self)),
-        }
+        no_expansion(self)
+    }
+}
+
+impl<'a> Replacer for &'a String {
+    fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
+        self.as_str().replace_append(caps, dst)
+    }
+
+    fn no_expansion(&mut self) -> Option<Cow<str>> {
+        no_expansion(self)
+    }
+}
+
+impl Replacer for String {
+    fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
+        self.as_str().replace_append(caps, dst)
+    }
+
+    fn no_expansion(&mut self) -> Option<Cow<str>> {
+        no_expansion(self)
+    }
+}
+
+impl<'a> Replacer for Cow<'a, str> {
+    fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
+        self.as_ref().replace_append(caps, dst)
+    }
+
+    fn no_expansion(&mut self) -> Option<Cow<str>> {
+        no_expansion(self)
+    }
+}
+
+impl<'a> Replacer for &'a Cow<'a, str> {
+    fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
+        self.as_ref().replace_append(caps, dst)
+    }
+
+    fn no_expansion(&mut self) -> Option<Cow<str>> {
+        no_expansion(self)
+    }
+}
+
+fn no_expansion<T: AsRef<str>>(t: &T) -> Option<Cow<str>> {
+    let s = t.as_ref();
+    match find_byte(b'$', s.as_bytes()) {
+        Some(_) => None,
+        None => Some(Cow::Borrowed(s)),
     }
 }
 
@@ -1215,6 +1288,7 @@ where
 /// and performant (since capture groups don't need to be found).
 ///
 /// `'t` is the lifetime of the literal text.
+#[derive(Clone, Debug)]
 pub struct NoExpand<'t>(pub &'t str);
 
 impl<'t> Replacer for NoExpand<'t> {
diff --git a/src/sparse.rs b/src/sparse.rs
index bc1b2b5..421d6b6 100644
--- a/src/sparse.rs
+++ b/src/sparse.rs
@@ -8,7 +8,7 @@ use std::slice;
 /// entire set can also be done in constant time. Iteration yields elements
 /// in the order in which they were inserted.
 ///
-/// The data structure is based on: http://research.swtch.com/sparse
+/// The data structure is based on: https://research.swtch.com/sparse
 /// Note though that we don't actually use uninitialized memory. We generally
 /// reuse allocations, so the initial allocation cost is bareable. However,
 /// its other properties listed above are extremely useful.


Test patch:
diff --git a/tests/macros_bytes.rs b/tests/macros_bytes.rs
index 03c370d..3d6c8c3 100644
--- a/tests/macros_bytes.rs
+++ b/tests/macros_bytes.rs
@@ -4,7 +4,6 @@ macro_rules! t { ($re:expr) => { text!($re) } }
 macro_rules! match_text { ($text:expr) => { $text.as_bytes() } }
 macro_rules! use_ { ($($path: tt)*) => { use regex::bytes::$($path)*; } }
 macro_rules! empty_vec { () => { <Vec<&[u8]>>::new() } }
-
 macro_rules! bytes { ($text:expr) => { $text } }
 
 macro_rules! no_expand {
diff --git a/tests/macros_str.rs b/tests/macros_str.rs
index 9b996b3..7b7eb11 100644
--- a/tests/macros_str.rs
+++ b/tests/macros_str.rs
@@ -4,6 +4,7 @@ macro_rules! t { ($text:expr) => { text!($text) } }
 macro_rules! match_text { ($text:expr) => { $text.as_str() } }
 macro_rules! use_ { ($($path: tt)*) => { use regex::$($path)*; } }
 macro_rules! empty_vec { () => { <Vec<&str>>::new() } }
+macro_rules! bytes { ($text:expr) => { std::str::from_utf8($text.as_ref()).unwrap() } }
 
 macro_rules! no_expand {
     ($text:expr) => {{
diff --git a/tests/replace.rs b/tests/replace.rs
index c156a39..700aff2 100644
--- a/tests/replace.rs
+++ b/tests/replace.rs
@@ -130,3 +130,101 @@ replace!(
     t!("${1}a $1a"),
     "ba "
 );
+
+replace!(
+    impl_string,
+    replace,
+    r"[0-9]",
+    "age: 26",
+    t!("Z".to_string()),
+    "age: Z6"
+);
+replace!(
+    impl_string_ref,
+    replace,
+    r"[0-9]",
+    "age: 26",
+    t!(&"Z".to_string()),
+    "age: Z6"
+);
+replace!(
+    impl_cow_str_borrowed,
+    replace,
+    r"[0-9]",
+    "age: 26",
+    t!(std::borrow::Cow::<'_, str>::Borrowed("Z")),
+    "age: Z6"
+);
+replace!(
+    impl_cow_str_borrowed_ref,
+    replace,
+    r"[0-9]",
+    "age: 26",
+    t!(&std::borrow::Cow::<'_, str>::Borrowed("Z")),
+    "age: Z6"
+);
+replace!(
+    impl_cow_str_owned,
+    replace,
+    r"[0-9]",
+    "age: 26",
+    t!(std::borrow::Cow::<'_, str>::Owned("Z".to_string())),
+    "age: Z6"
+);
+replace!(
+    impl_cow_str_owned_ref,
+    replace,
+    r"[0-9]",
+    "age: 26",
+    t!(&std::borrow::Cow::<'_, str>::Owned("Z".to_string())),
+    "age: Z6"
+);
+
+replace!(
+    impl_vec_u8,
+    replace,
+    r"[0-9]",
+    "age: 26",
+    bytes!(vec![b'Z']),
+    "age: Z6"
+);
+replace!(
+    impl_vec_u8_ref,
+    replace,
+    r"[0-9]",
+    "age: 26",
+    bytes!(&vec![b'Z']),
+    "age: Z6"
+);
+replace!(
+    impl_cow_slice_borrowed,
+    replace,
+    r"[0-9]",
+    "age: 26",
+    bytes!(std::borrow::Cow::<'_, [u8]>::Borrowed(&[b'Z'])),
+    "age: Z6"
+);
+replace!(
+    impl_cow_slice_borrowed_ref,
+    replace,
+    r"[0-9]",
+    "age: 26",
+    bytes!(&std::borrow::Cow::<'_, [u8]>::Borrowed(&[b'Z'])),
+    "age: Z6"
+);
+replace!(
+    impl_cow_slice_owned,
+    replace,
+    r"[0-9]",
+    "age: 26",
+    bytes!(std::borrow::Cow::<'_, [u8]>::Owned(vec![b'Z'])),
+    "age: Z6"
+);
+replace!(
+    impl_cow_slice_owned_ref,
+    replace,
+    r"[0-9]",
+    "age: 26",
+    bytes!(&std::borrow::Cow::<'_, [u8]>::Owned(vec![b'Z'])),
+    "age: Z6"
+);