Repository: rust-bakery/nom (language: rust)

Issue:
Consolidate parser variants using ranges (e.g. `many0`, `many_m_n`)
## Prerequisites

Here are a few things you should provide to help me understand the issue:

- Rust version : 1.44
- nom version : nom7
- nom compilation features used: basic

## Idea

Nom has `many1`, `many0`, and `many_m_n` functions.  Similar with other parts of the API.

What if instead we had a `IntoRange` trait that took in the different range types and single numbers.

Example:
```rust
many(tag("abc"), 0..) -> many0(tag("abc")) 
many(tag("abc"), 1..) -> many1(tag("abc")) 
many(tag("abc"), 1) -> many_m_n(tag("abc"), 1, 1) 
```


Discussion / hints:
I quite like this idea. At the very least this makes the API much cleaner by unifying the very similar parsers.

This could also be done for the `fold_*` family of parsers since they work very similarly to `many_*` and ranges would bring the same benefit to them.

However Rust Ranges are [~~a nightmare~~ slightly prone to footguns](https://ridiculousfish.com/blog/posts/least-favorite-rust-type.html). I see the following points that need to be taken into account:

- [ ] Anything can be a Range.
  In the case of `many_*` it should probably only take a Range of `usize`. It needs to pack the result into a `Vec` so `usize` makes the most sense and prevents passing invalid types accidentally.
For `fold_*` it should be limited to any integer type. `fold_*` is not limited by memory since it only has one accumulator value and as such can safely run for longer than `usize` times (this may even be desireable on systems with very small `usize` limits). So perhaps something like an `IntoIntRange` trait? I dont think nom would currently benefit from other range types, such as floats, but more traits could be introduced later on if desired.

- [ ] Open-ended ranges.
Open-ended ranges (`1..`) with `many_*` could exhibit the behavior you described (be equivalent to `1..usize::MAX`). Alternatively they could fail once they would exceed the `usize` limit.
However the `fold_*` parsers could theoretically run for an infinite amount of cycles, even beyond the limit of its integer type. While this might not be a very common case it is still feasible and the current implementation allows such cases. So I would like to preserve that behavior with ranges as well.
Because of these points any implementation of this should also supply the parser with information about the exact bounds of the range.
Perhaps the exact behavior of unbounded ranges is best offloaded onto the parser. Any parser can then handle such special cases itself and then document them individually. That would allow for the most amount of flexibility and prevents conflicting interpretation of range bounds.

As an afterthought, your example has a parameter order of `many(parser, range)`. I would switch those around to `many(range, parser)`. This is more consistent with how the other multi combinators handle parameters and makes nested parsers much more readable:
```rust
many(1..5,
  alt((
    tag("+"),
    tag("-"),
  ))
)
```
A fold version would look similar:
```rust
fold(1..5,
  Vec::new,
  tag("+"),
  |acc: Vec<_>, s: &str| {
    acc.push(s);
    acc
  }
)
```
Also the issue title really doesnt fit what you describe. Perhaps rephrase it to something more clear like "Unify nom::multi parsers with ranges"?
> In the case of many_* it should probably only take a Range of usize. It needs to pack the result into a Vec so usize makes the most sense and prevents passing invalid types accidentally.

Yes, I assume the range would just be for the existing type of the type

> However the fold_* parsers could theoretically run for an infinite amount of cycles, even beyond the limit of its integer type. While this might not be a very common case it is still feasible and the current implementation allows such cases. So I would like to preserve that behavior with ranges as well.

Oh, I wasn't aware of that parser, so yes, we'd need to handle going beyond usize::MAX

> As an afterthought, your example has a parameter order of many(parser, range). I would switch those around to many(range, parser). This is more consistent with how the other multi combinators handle parameters and makes nested parsers much more readable:

My ordering was not meant to be prescriptive; I was just going off of a faulty memory
To double check, do we feel its worth I go ahead and put together a PR?
> do we feel its worth I go ahead and put together a PR

Im just a random contributor, but for what its worth I like this idea enough to want to make a PR myself. So I would say yes.

> Yes, I assume the range would just be for the existing type of the type

For `many` thats fine. I would see some benefit to allowing arbitrary integer types for `fold`, such as wanting to use a 64-bit range on a 32-bit platform. But I suppose thats not possible in the current implementation anyway since `fold_many_m_n` takes `usize` as arguments as well. So I guess its fine to stay with `usize` ranges only. This can always be augmented later. However an infinite upper bound should still behave properly.
My plan is to create a PR for one parser to get feedback on the design and approach and then do one PR for each additional parser.

Candidates:
- `nom::bytes::complete` and `nom::bytes::streaming`'s `take_till` and `take_while`
- `nom::multi`s `count`, `fold_many`, `many`, `many_count`

More questionable cases:
- `nom::multi::many_till` parallels `nom::bytes::*::take_till` but doesn't have count variants
- `nom::character` and `nom::multi::separated_list`, `nom::bytes::*::take_*til` have `0`/`1` variants but not `m_n`.  I'd be tempted to include them because I have run into cases where I cap these.

The main open question is on naming.  Dropping the suffix would work in cases like `nom::multi::many` but for `nom::bytes::*::take_till*`, the unsuffixed version takes a predicate.  Options
- Hold off on these cases, which would lead to inconsistent naming
- Add ranges to the predicate-only versions, requiring the user to pass `..`
- Decide on a new suffix (`_n`, `_r`, `_ranged`?)
> inconsistent naming

That problem runs much deeper than just this. Take `count` and `many_count` for example. One runs a parser a set number of times, the other counts how often the child parser has ran. (`count` should realy be called `collect` or `many_m`)
Personally I think its more important to stay (somewhat) consistent with the preexisting parser names rather than trying to come up with a unifying naming scheme for "takes a range".

> nom::multi::{count, many0, many1, many_m_n}

Are all covered by `many`. `count` is just the `many_m_n` variant but with a single run count instead of a range. I think `many` as a name is sufficient, it clearly conveys that it is related to the existing `many_*` parsers and reading it in code makes its function clear.

> nom::multi::{fold_many0, fold_many1, fold_many_m_n}

Can all be covered by a universal `fold` parser taking a range. Argument for `fold` as a name is the same as with `many`.

> nom::bytes::complete::{take_till, take_while}
> nom::multi::many_till

Maybe? The `take_till` vs `take_till1` certainly implies that it could be improved with ranges. `many_till` is basically just `take_till` but as a loop over parsers instead of over bytes. And `take_while` is just `take_till` but with the condition inverted. These are all basically the same use case just typed slightly different so either we do them all or none of them.

Naming in these cases is an issue, primarily because nom breaks its own naming convention (again) by naming it `take_till` instead of `take_till0` (maybe this just grew historically?). Even with this I still think `many` and `fold` are good names. We can still come up with something new for these.
> Adding ranges to the predicate-only versions

Probably not that one. It would be better to introduce the ranged parsers as completely new and then deprecate the old ones, which avoids breaking backwards compatibility.
> For `fold_*` it should be limited to any integer type. `fold_*` is not limited by memory since it only has one accumulator value and as such can safely run for longer than `usize` times (this may even be desireable on systems with very small `usize` limits). So perhaps something like an `IntoIntRange` trait? I dont think nom would currently benefit from other range types, such as floats, but more traits could be introduced later on if desired.

`usize` represent a size, de facto, it's should be used anywhere you need to count, so there is absolute no good argument to allow signed  integer here.

> As an afterthought, your example has a parameter order of many(parser, range). I would switch those around to many(range, parser). This is more consistent with how the other multi combinators handle parameters and makes nested parsers much more readable:

Could we make this `many(range)(parser)(input)` allowing complex composition.
> usize represent a size, de facto, it's should be used anywhere you need to count, so there is absolute no good argument to allow signed integer here.

It was less about signedness and more about size. Specifically the fact that `fold_*` can run until infinity and therefore exceed the size of any integer type. Negative integers do not make sense here so I didnt mention it explicitly.

> Could we make this many(range)(parser)(input) allowing complex composition.

What kind of composition would that be? And what exactly is the type of the intermediary `many(range)` and what advantages does it offer?
What would be the respective type of `fold(range)`? Or for one of the `take_*` parsers?

I quite frankly see no reason why that should be split into two types and as far as I know this pattern isnt used anywhere else in nom.

Fix patch:
diff --git a/benchmarks/benches/arithmetic.rs b/benchmarks/benches/arithmetic.rs
index cab12224f..2bb44515f 100644
--- a/benchmarks/benches/arithmetic.rs
+++ b/benchmarks/benches/arithmetic.rs
@@ -9,7 +9,7 @@ use nom::{
   branch::alt,
   character::complete::{char, digit1, one_of, space0},
   combinator::map_res,
-  multi::fold_many0,
+  multi::fold,
   sequence::{delimited, pair},
   IResult,
 };
@@ -37,7 +37,8 @@ fn factor(input: &[u8]) -> IResult<&[u8], i64> {
 // the math by folding everything
 fn term(input: &[u8]) -> IResult<&[u8], i64> {
   let (input, init) = factor(input)?;
-  fold_many0(
+  fold(
+    0..,
     pair(one_of("*/"), factor),
     move || init,
     |acc, (op, val)| {
@@ -52,7 +53,8 @@ fn term(input: &[u8]) -> IResult<&[u8], i64> {
 
 fn expr(input: &[u8]) -> IResult<&[u8], i64> {
   let (input, init) = term(input)?;
-  fold_many0(
+  fold(
+    0..,
     pair(one_of("+-"), term),
     move || init,
     |acc, (op, val)| {
diff --git a/benchmarks/benches/http.rs b/benchmarks/benches/http.rs
index 4145d4a7b..fd26893a1 100644
--- a/benchmarks/benches/http.rs
+++ b/benchmarks/benches/http.rs
@@ -4,7 +4,7 @@
 static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
 
 use criterion::*;
-use nom::{IResult, bytes::complete::{tag, take_while1}, character::complete::{line_ending, char}, multi::many1};
+use nom::{IResult, bytes::complete::{tag, take_while1}, character::complete::{line_ending, char}, multi::many};
 
 #[cfg_attr(rustfmt, rustfmt_skip)]
 #[derive(Debug)]
@@ -96,14 +96,14 @@ fn message_header_value(input: &[u8]) -> IResult<&[u8], &[u8]> {
 fn message_header(input: &[u8]) -> IResult<&[u8], Header<'_>> {
   let (input, name) = take_while1(is_token)(input)?;
   let (input, _) = char(':')(input)?;
-  let (input, value) = many1(message_header_value)(input)?;
+  let (input, value) = many(1.., message_header_value)(input)?;
 
   Ok((input, Header{ name, value }))
 }
 
 fn request(input: &[u8]) -> IResult<&[u8], (Request<'_>, Vec<Header<'_>>)> {
   let (input, req) = request_line(input)?;
-  let (input, h) = many1(message_header)(input)?;
+  let (input, h) = many(1.., message_header)(input)?;
   let (input, _) = line_ending(input)?;
 
   Ok((input, (req, h)))
diff --git a/benchmarks/benches/ini.rs b/benchmarks/benches/ini.rs
index b2f651b41..95fd95cd0 100644
--- a/benchmarks/benches/ini.rs
+++ b/benchmarks/benches/ini.rs
@@ -9,7 +9,7 @@ use nom::{
     alphanumeric1 as alphanumeric, char, multispace1 as multispace, space1 as space,
   },
   combinator::{map, map_res, opt},
-  multi::many0,
+  multi::many,
   sequence::{delimited, pair, separated_pair, terminated, tuple},
   IResult,
 };
@@ -33,14 +33,17 @@ fn key_value(i: &[u8]) -> IResult<&[u8], (&str, &str)> {
 
 fn categories(i: &[u8]) -> IResult<&[u8], HashMap<&str, HashMap<&str, &str>>> {
   map(
-    many0(separated_pair(
-      category,
-      opt(multispace),
-      map(
-        many0(terminated(key_value, opt(multispace))),
-        |vec: Vec<_>| vec.into_iter().collect(),
+    many(
+      0..,
+      separated_pair(
+        category,
+        opt(multispace),
+        map(
+          many(0.., terminated(key_value, opt(multispace))),
+          |vec: Vec<_>| vec.into_iter().collect(),
+        ),
       ),
-    )),
+    ),
     |vec: Vec<_>| vec.into_iter().collect(),
   )(i)
 }
@@ -70,7 +73,7 @@ file=payroll.dat
 \0";
 
   fn acc(i: &[u8]) -> IResult<&[u8], Vec<(&str, &str)>> {
-    many0(key_value)(i)
+    many(0.., key_value)(i)
   }
 
   let mut group = c.benchmark_group("ini keys and values");
diff --git a/benchmarks/benches/ini_str.rs b/benchmarks/benches/ini_str.rs
index 7df62194a..82dce2efa 100644
--- a/benchmarks/benches/ini_str.rs
+++ b/benchmarks/benches/ini_str.rs
@@ -7,7 +7,7 @@ use nom::{
   bytes::complete::{is_a, tag, take_till, take_while},
   character::complete::{alphanumeric1 as alphanumeric, char, not_line_ending, space0 as space},
   combinator::opt,
-  multi::many0,
+  multi::many,
   sequence::{delimited, pair, terminated, tuple},
   IResult,
 };
@@ -40,7 +40,7 @@ fn key_value(i: &str) -> IResult<&str, (&str, &str)> {
 }
 
 fn keys_and_values_aggregator(i: &str) -> IResult<&str, Vec<(&str, &str)>> {
-  many0(key_value)(i)
+  many(0.., key_value)(i)
 }
 
 fn keys_and_values(input: &str) -> IResult<&str, HashMap<&str, &str>> {
@@ -55,7 +55,7 @@ fn category_and_keys(i: &str) -> IResult<&str, (&str, HashMap<&str, &str>)> {
 }
 
 fn categories_aggregator(i: &str) -> IResult<&str, Vec<(&str, HashMap<&str, &str>)>> {
-  many0(category_and_keys)(i)
+  many(0.., category_and_keys)(i)
 }
 
 fn categories(input: &str) -> IResult<&str, HashMap<&str, HashMap<&str, &str>>> {
diff --git a/benchmarks/benches/json.rs b/benchmarks/benches/json.rs
index 7dd3f5a18..3d4f44a74 100644
--- a/benchmarks/benches/json.rs
+++ b/benchmarks/benches/json.rs
@@ -11,7 +11,7 @@ use nom::{
   character::complete::{anychar, char, multispace0, none_of},
   combinator::{map, map_opt, map_res, value, verify},
   error::{ErrorKind, ParseError},
-  multi::{fold_many0, separated_list0},
+  multi::{fold, separated_list0},
   number::complete::{double, recognize_float},
   sequence::{delimited, preceded, separated_pair},
   IResult, Parser,
@@ -87,7 +87,7 @@ fn character(input: &str) -> IResult<&str, char> {
 fn string(input: &str) -> IResult<&str, String> {
   delimited(
     char('"'),
-    fold_many0(character, String::new, |mut string, c| {
+    fold(0.., character, String::new, |mut string, c| {
       string.push(c);
       string
     }),
diff --git a/examples/s_expression.rs b/examples/s_expression.rs
index 7f4595186..be2398437 100644
--- a/examples/s_expression.rs
+++ b/examples/s_expression.rs
@@ -10,7 +10,7 @@ use nom::{
   character::complete::{alpha1, char, digit1, multispace0, multispace1, one_of},
   combinator::{cut, map, map_res, opt},
   error::{context, VerboseError},
-  multi::many0,
+  multi::many,
   sequence::{delimited, preceded, terminated},
   IResult, Parser,
 };
@@ -174,7 +174,7 @@ where
 /// parser returning a tuple containing the result of each parser in the same order,
 /// and then map over it to transform the output into an `Expr::Application`
 fn parse_application(i: &str) -> IResult<&str, Expr, VerboseError<&str>> {
-  let application_inner = map((parse_expr, many0(parse_expr)), |(head, tail)| {
+  let application_inner = map((parse_expr, many(0.., parse_expr)), |(head, tail)| {
     Expr::Application(Box::new(head), tail)
   });
   // finally, we wrap it in an s-expression
@@ -224,7 +224,10 @@ fn parse_quote(i: &str) -> IResult<&str, Expr, VerboseError<&str>> {
   // we find the `'` (quote) character, use cut to say that we're unambiguously
   // looking for an s-expression of 0 or more expressions, and then parse them
   map(
-    context("quote", preceded(tag("'"), cut(s_exp(many0(parse_expr))))),
+    context(
+      "quote",
+      preceded(tag("'"), cut(s_exp(many(0.., parse_expr)))),
+    ),
     Expr::Quote,
   )(i)
 }
diff --git a/examples/string.rs b/examples/string.rs
index b4015c571..396b3e674 100644
--- a/examples/string.rs
+++ b/examples/string.rs
@@ -16,7 +16,7 @@ use nom::bytes::streaming::{is_not, take_while_m_n};
 use nom::character::streaming::{char, multispace1};
 use nom::combinator::{map, map_opt, map_res, value, verify};
 use nom::error::{FromExternalError, ParseError};
-use nom::multi::fold_many0;
+use nom::multi::fold;
 use nom::sequence::{delimited, preceded};
 use nom::IResult;
 
@@ -136,9 +136,10 @@ fn parse_string<'a, E>(input: &'a str) -> IResult<&'a str, String, E>
 where
   E: ParseError<&'a str> + FromExternalError<&'a str, std::num::ParseIntError>,
 {
-  // fold_many0 is the equivalent of iterator::fold. It runs a parser in a loop,
+  // fold is the equivalent of iterator::fold. It runs a parser in a loop,
   // and for each output value, calls a folding function on each output value.
-  let build_string = fold_many0(
+  let build_string = fold(
+    0..,
     // Our parser function– parses a single string fragment
     parse_fragment,
     // Our init value, an empty string
@@ -157,7 +158,7 @@ where
 
   // Finally, parse the string. Note that, if `build_string` could accept a raw
   // " character, the closing delimiter " would never match. When using
-  // `delimited` with a looping parser (like fold_many0), be sure that the
+  // `delimited` with a looping parser (like fold), be sure that the
   // loop won't accidentally match your closing delimiter!
   delimited(char('"'), build_string, char('"'))(input)
 }
diff --git a/src/error.rs b/src/error.rs
index f0bb26a7b..cd9a0273c 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -417,6 +417,8 @@ pub enum ErrorKind {
   Float,
   Satisfy,
   Fail,
+  Many,
+  Fold,
 }
 
 #[rustfmt::skip]
@@ -477,6 +479,8 @@ pub fn error_to_u32(e: &ErrorKind) -> u32 {
     ErrorKind::Float                     => 73,
     ErrorKind::Satisfy                   => 74,
     ErrorKind::Fail                      => 75,
+    ErrorKind::Many                      => 76,
+    ErrorKind::Fold                      => 77,
   }
 }
 
@@ -539,6 +543,8 @@ impl ErrorKind {
       ErrorKind::Float                     => "Float",
       ErrorKind::Satisfy                   => "Satisfy",
       ErrorKind::Fail                      => "Fail",
+      ErrorKind::Many                      => "Many",
+      ErrorKind::Fold                      => "Fold",
     }
   }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 9274f1e9c..df84ebf34 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -408,7 +408,7 @@ pub mod lib {
     pub use alloc::{borrow, boxed, string, vec};
 
     #[doc(hidden)]
-    pub use core::{cmp, convert, fmt, iter, mem, ops, option, result, slice, str};
+    pub use core::{cmp, convert, fmt, iter, mem, num, ops, option, result, slice, str};
 
     /// internal reproduction of std prelude
     #[doc(hidden)]
@@ -423,8 +423,8 @@ pub mod lib {
   pub mod std {
     #[doc(hidden)]
     pub use std::{
-      alloc, borrow, boxed, cmp, collections, convert, fmt, hash, iter, mem, ops, option, result,
-      slice, str, string, vec,
+      alloc, borrow, boxed, cmp, collections, convert, fmt, hash, iter, mem, num, ops, option,
+      result, slice, str, string, vec,
     };
 
     /// internal reproduction of std prelude
diff --git a/src/multi/mod.rs b/src/multi/mod.rs
index ce23ae993..4ad2a4cea 100644
--- a/src/multi/mod.rs
+++ b/src/multi/mod.rs
@@ -6,10 +6,15 @@ mod tests;
 use crate::error::ErrorKind;
 use crate::error::ParseError;
 use crate::internal::{Err, IResult, Needed, Parser};
+use crate::lib::std::num::NonZeroUsize;
+#[cfg(feature = "alloc")]
+use crate::lib::std::ops::Bound;
 #[cfg(feature = "alloc")]
 use crate::lib::std::vec::Vec;
-use crate::traits::{InputLength, InputTake, ToUsize};
-use core::num::NonZeroUsize;
+use crate::{
+  traits::{InputLength, InputTake, ToUsize},
+  NomRange,
+};
 
 /// Don't pre-allocate more than 64KiB when calling `Vec::with_capacity`.
 ///
@@ -881,7 +886,6 @@ where
           acc = fold(acc, value);
           input = tail;
         }
-        //FInputXMError: handle failure properly
         Err(Err::Error(err)) => {
           if count < min {
             return Err(Err::Error(E::append(input, ErrorKind::ManyMN, err)));
@@ -1045,3 +1049,166 @@ where
     Ok((input, res))
   }
 }
+
+/// Repeats the embedded parser and returns the results in a `Vec`.
+/// Fails if the amount of time the embedded parser is run is not
+/// within the specified range.
+/// # Arguments
+/// * `range` Constrains the number of iterations.
+///   * A range without an upper bound `a..` is equivalent to a range of `a..=usize::MAX`.
+///   * A single `usize` value is equivalent to `value..=value`.
+///   * An empty range is invalid.
+/// * `parse` The parser to apply.
+/// ```rust
+/// # #[macro_use] extern crate nom;
+/// # use nom::{Err, error::ErrorKind, Needed, IResult};
+/// use nom::multi::many;
+/// use nom::bytes::complete::tag;
+///
+/// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
+///   many(0..=2, tag("abc"))(s)
+/// }
+///
+/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"])));
+/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"])));
+/// assert_eq!(parser("123123"), Ok(("123123", vec![])));
+/// assert_eq!(parser(""), Ok(("", vec![])));
+/// assert_eq!(parser("abcabcabc"), Ok(("abc", vec!["abc", "abc"])));
+/// ```
+#[cfg(feature = "alloc")]
+#[cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))]
+pub fn many<I, O, E, F, G>(range: G, mut parse: F) -> impl FnMut(I) -> IResult<I, Vec<O>, E>
+where
+  I: Clone + InputLength,
+  F: Parser<I, O, E>,
+  E: ParseError<I>,
+  G: NomRange<usize>,
+{
+  let capacity = match range.bounds() {
+    (Bound::Included(start), _) => start,
+    (Bound::Excluded(start), _) => start + 1,
+    _ => 4,
+  };
+
+  move |mut input: I| {
+    if range.is_inverted() {
+      return Err(Err::Failure(E::from_error_kind(input, ErrorKind::Many)));
+    }
+
+    let max_initial_capacity = MAX_INITIAL_CAPACITY_BYTES / crate::lib::std::mem::size_of::<O>();
+    let mut res = crate::lib::std::vec::Vec::with_capacity(capacity.min(max_initial_capacity));
+
+    for count in range.bounded_iter() {
+      let len = input.input_len();
+      match parse.parse(input.clone()) {
+        Ok((tail, value)) => {
+          // infinite loop check: the parser must always consume
+          if tail.input_len() == len {
+            return Err(Err::Error(E::from_error_kind(input, ErrorKind::Many)));
+          }
+
+          res.push(value);
+          input = tail;
+        }
+        Err(Err::Error(e)) => {
+          if !range.contains(&count) {
+            return Err(Err::Error(E::append(input, ErrorKind::Many, e)));
+          } else {
+            return Ok((input, res));
+          }
+        }
+        Err(e) => {
+          return Err(e);
+        }
+      }
+    }
+
+    Ok((input, res))
+  }
+}
+
+/// Applies a parser and accumulates the results using a given
+/// function and initial value.
+/// Fails if the amount of time the embedded parser is run is not
+/// within the specified range.
+///
+/// # Arguments
+/// * `range` Constrains the number of iterations.
+///   * A range without an upper bound `a..` allows the parser to run until it fails.
+///   * A single `usize` value is equivalent to `value..=value`.
+///   * An empty range is invalid.
+/// * `parse` The parser to apply.
+/// * `init` A function returning the initial value.
+/// * `fold` The function that combines a result of `f` with
+///       the current accumulator.
+/// ```rust
+/// # #[macro_use] extern crate nom;
+/// # use nom::{Err, error::ErrorKind, Needed, IResult};
+/// use nom::multi::fold;
+/// use nom::bytes::complete::tag;
+///
+/// fn parser(s: &str) -> IResult<&str, Vec<&str>> {
+///   fold(
+///     0..=2,
+///     tag("abc"),
+///     Vec::new,
+///     |mut acc: Vec<_>, item| {
+///       acc.push(item);
+///       acc
+///     }
+///   )(s)
+/// }
+///
+/// assert_eq!(parser("abcabc"), Ok(("", vec!["abc", "abc"])));
+/// assert_eq!(parser("abc123"), Ok(("123", vec!["abc"])));
+/// assert_eq!(parser("123123"), Ok(("123123", vec![])));
+/// assert_eq!(parser(""), Ok(("", vec![])));
+/// assert_eq!(parser("abcabcabc"), Ok(("abc", vec!["abc", "abc"])));
+/// ```
+pub fn fold<I, O, E, F, G, H, J, R>(
+  range: J,
+  mut parse: F,
+  mut init: H,
+  mut fold: G,
+) -> impl FnMut(I) -> IResult<I, R, E>
+where
+  I: Clone + InputLength,
+  F: Parser<I, O, E>,
+  G: FnMut(R, O) -> R,
+  H: FnMut() -> R,
+  E: ParseError<I>,
+  J: NomRange<usize>,
+{
+  move |mut input: I| {
+    if range.is_inverted() {
+      return Err(Err::Failure(E::from_error_kind(input, ErrorKind::Fold)));
+    }
+
+    let mut acc = init();
+
+    for count in range.saturating_iter() {
+      let len = input.input_len();
+      match parse.parse(input.clone()) {
+        Ok((tail, value)) => {
+          // infinite loop check: the parser must always consume
+          if tail.input_len() == len {
+            return Err(Err::Error(E::from_error_kind(tail, ErrorKind::Fold)));
+          }
+
+          acc = fold(acc, value);
+          input = tail;
+        }
+        Err(Err::Error(err)) => {
+          if !range.contains(&count) {
+            return Err(Err::Error(E::append(input, ErrorKind::Fold, err)));
+          } else {
+            break;
+          }
+        }
+        Err(e) => return Err(e),
+      }
+    }
+
+    Ok((input, acc))
+  }
+}
diff --git a/src/str.rs b/src/str.rs
index 575a7eab3..1ee7cef9f 100644
--- a/src/str.rs
+++ b/src/str.rs
@@ -1,7 +1,7 @@
 #[cfg(test)]
 mod test {
   #[cfg(feature = "alloc")]
-  use crate::{branch::alt, bytes::complete::tag_no_case, combinator::recognize, multi::many1};
+  use crate::{branch::alt, bytes::complete::tag_no_case, combinator::recognize, multi::many};
   use crate::{
     bytes::complete::{is_a, is_not, tag, take, take_till, take_until},
     error::{self, ErrorKind},
@@ -491,7 +491,7 @@ mod test {
     let b = "ababcd";
 
     fn f(i: &str) -> IResult<&str, &str> {
-      recognize(many1(alt((tag("a"), tag("b")))))(i)
+      recognize(many(1.., alt((tag("a"), tag("b")))))(i)
     }
 
     assert_eq!(f(a), Ok((&a[6..], a)));
diff --git a/src/traits.rs b/src/traits.rs
index 43d175c42..e61c57a2a 100644
--- a/src/traits.rs
+++ b/src/traits.rs
@@ -2,7 +2,9 @@
 use crate::error::{ErrorKind, ParseError};
 use crate::internal::{Err, IResult, Needed};
 use crate::lib::std::iter::{Copied, Enumerate};
-use crate::lib::std::ops::{Range, RangeFrom, RangeFull, RangeTo};
+use crate::lib::std::ops::{
+  Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
+};
 use crate::lib::std::slice::Iter;
 use crate::lib::std::str::from_utf8;
 use crate::lib::std::str::CharIndices;
@@ -1408,6 +1410,241 @@ impl HexDisplay for str {
   }
 }
 
+/// A saturating iterator for usize.
+pub struct SaturatingIterator {
+  count: usize,
+}
+
+impl Iterator for SaturatingIterator {
+  type Item = usize;
+
+  fn next(&mut self) -> Option<Self::Item> {
+    let old_count = self.count;
+    self.count = self.count.saturating_add(1);
+    Some(old_count)
+  }
+}
+
+/// Abstractions for range-like types.
+pub trait NomRange<Idx> {
+  /// The saturating iterator type.
+  type Saturating: Iterator<Item = Idx>;
+  /// The bounded iterator type.
+  type Bounded: Iterator<Item = Idx>;
+
+  /// `true` if `item` is contained in the range.
+  fn contains(&self, item: &Idx) -> bool;
+
+  /// Returns the bounds of this range.
+  fn bounds(&self) -> (Bound<Idx>, Bound<Idx>);
+
+  /// `true` if the range is inverted.
+  fn is_inverted(&self) -> bool;
+
+  /// Creates a saturating iterator.
+  /// A saturating iterator counts the number of iterations starting from 0 up to the upper bound of this range.
+  /// If the upper bound is infinite the iterator saturates at the largest representable value of its type and
+  /// returns it for all further elements.
+  fn saturating_iter(&self) -> Self::Saturating;
+
+  /// Creates a bounded iterator.
+  /// A bounded iterator counts the number of iterations starting from 0 up to the upper bound of this range.
+  /// If the upper bounds is infinite the iterator counts up until the amount of iterations has reached the
+  /// largest representable value of its type and then returns `None` for all further elements.
+  fn bounded_iter(&self) -> Self::Bounded;
+}
+
+impl NomRange<usize> for Range<usize> {
+  type Saturating = Range<usize>;
+  type Bounded = Range<usize>;
+
+  fn bounds(&self) -> (Bound<usize>, Bound<usize>) {
+    (Bound::Included(self.start), Bound::Excluded(self.end))
+  }
+
+  fn contains(&self, item: &usize) -> bool {
+    RangeBounds::contains(self, item)
+  }
+
+  fn is_inverted(&self) -> bool {
+    !(self.start < self.end)
+  }
+
+  fn saturating_iter(&self) -> Self::Saturating {
+    if self.end == 0 {
+      1..0
+    } else {
+      0..self.end - 1
+    }
+  }
+
+  fn bounded_iter(&self) -> Self::Bounded {
+    if self.end == 0 {
+      1..0
+    } else {
+      0..self.end - 1
+    }
+  }
+}
+
+impl NomRange<usize> for RangeInclusive<usize> {
+  type Saturating = Range<usize>;
+  type Bounded = Range<usize>;
+
+  fn bounds(&self) -> (Bound<usize>, Bound<usize>) {
+    (Bound::Included(*self.start()), Bound::Included(*self.end()))
+  }
+
+  fn contains(&self, item: &usize) -> bool {
+    RangeBounds::contains(self, item)
+  }
+
+  fn is_inverted(&self) -> bool {
+    !RangeInclusive::contains(self, self.start())
+  }
+
+  fn saturating_iter(&self) -> Self::Saturating {
+    0..*self.end()
+  }
+
+  fn bounded_iter(&self) -> Self::Bounded {
+    0..*self.end()
+  }
+}
+
+impl NomRange<usize> for RangeFrom<usize> {
+  type Saturating = SaturatingIterator;
+  type Bounded = Range<usize>;
+
+  fn bounds(&self) -> (Bound<usize>, Bound<usize>) {
+    (Bound::Included(self.start), Bound::Unbounded)
+  }
+
+  fn contains(&self, item: &usize) -> bool {
+    RangeBounds::contains(self, item)
+  }
+
+  fn is_inverted(&self) -> bool {
+    false
+  }
+
+  fn saturating_iter(&self) -> Self::Saturating {
+    SaturatingIterator { count: 0 }
+  }
+
+  fn bounded_iter(&self) -> Self::Bounded {
+    0..core::usize::MAX
+  }
+}
+
+impl NomRange<usize> for RangeTo<usize> {
+  type Saturating = Range<usize>;
+  type Bounded = Range<usize>;
+
+  fn bounds(&self) -> (Bound<usize>, Bound<usize>) {
+    (Bound::Unbounded, Bound::Excluded(self.end))
+  }
+
+  fn contains(&self, item: &usize) -> bool {
+    RangeBounds::contains(self, item)
+  }
+
+  fn is_inverted(&self) -> bool {
+    false
+  }
+
+  fn saturating_iter(&self) -> Self::Saturating {
+    if self.end == 0 {
+      1..0
+    } else {
+      0..self.end - 1
+    }
+  }
+
+  fn bounded_iter(&self) -> Self::Bounded {
+    if self.end == 0 {
+      1..0
+    } else {
+      0..self.end - 1
+    }
+  }
+}
+
+impl NomRange<usize> for RangeToInclusive<usize> {
+  type Saturating = Range<usize>;
+  type Bounded = Range<usize>;
+
+  fn bounds(&self) -> (Bound<usize>, Bound<usize>) {
+    (Bound::Unbounded, Bound::Included(self.end))
+  }
+
+  fn contains(&self, item: &usize) -> bool {
+    RangeBounds::contains(self, item)
+  }
+
+  fn is_inverted(&self) -> bool {
+    false
+  }
+
+  fn saturating_iter(&self) -> Self::Saturating {
+    0..self.end
+  }
+
+  fn bounded_iter(&self) -> Self::Bounded {
+    0..self.end
+  }
+}
+
+impl NomRange<usize> for RangeFull {
+  type Saturating = SaturatingIterator;
+  type Bounded = Range<usize>;
+
+  fn bounds(&self) -> (Bound<usize>, Bound<usize>) {
+    (Bound::Unbounded, Bound::Unbounded)
+  }
+
+  fn contains(&self, item: &usize) -> bool {
+    RangeBounds::contains(self, item)
+  }
+
+  fn is_inverted(&self) -> bool {
+    false
+  }
+
+  fn saturating_iter(&self) -> Self::Saturating {
+    SaturatingIterator { count: 0 }
+  }
+
+  fn bounded_iter(&self) -> Self::Bounded {
+    0..core::usize::MAX
+  }
+}
+
+impl NomRange<usize> for usize {
+  type Saturating = Range<usize>;
+  type Bounded = Range<usize>;
+
+  fn bounds(&self) -> (Bound<usize>, Bound<usize>) {
+    (Bound::Included(*self), Bound::Included(*self))
+  }
+
+  fn contains(&self, item: &usize) -> bool {
+    self == item
+  }
+
+  fn is_inverted(&self) -> bool {
+    false
+  }
+
+  fn saturating_iter(&self) -> Self::Saturating {
+    0..*self
+  }
+
+  fn bounded_iter(&self) -> Self::Bounded {
+    0..*self
+  }
+}
+
 #[cfg(test)]
 mod tests {
   use super::*;


Test patch:
diff --git a/src/multi/tests.rs b/src/multi/tests.rs
index 39918c806..3f0c92525 100644
--- a/src/multi/tests.rs
+++ b/src/multi/tests.rs
@@ -13,7 +13,7 @@ use crate::{
   lib::std::vec::Vec,
   multi::{
     count, fold_many0, fold_many1, fold_many_m_n, length_count, many0, many1, many_m_n, many_till,
-    separated_list0, separated_list1,
+    separated_list0, separated_list1, many, fold,
   },
 };
 
@@ -182,7 +182,6 @@ fn many_till_test() {
 #[cfg(feature = "std")]
 fn infinite_many() {
   fn tst(input: &[u8]) -> IResult<&[u8], &[u8]> {
-    println!("input: {:?}", input);
     Err(Err::Error(error_position!(input, ErrorKind::Tag)))
   }
 
@@ -533,3 +532,320 @@ fn many1_count_test() {
     )))
   );
 }
+
+#[test]
+#[cfg(feature = "alloc")]
+fn many_test() {
+  // should not go into an infinite loop
+  fn many_error_0(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    fn tst(input: &[u8]) -> IResult<&[u8], &[u8]> {
+      Err(Err::Error(error_position!(input, ErrorKind::Tag)))
+    }
+    many(0.., tst)(i)
+  }
+  
+  let a = &b"abcdef"[..];
+  assert_eq!(many_error_0(a), Ok((a, Vec::new())));
+  
+  
+  fn many_error_1(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    fn tst(input: &[u8]) -> IResult<&[u8], &[u8]> {
+      Err(Err::Error(error_position!(input, ErrorKind::Tag)))
+    }
+    many(1.., tst)(i)
+  }
+  
+  let a = &b"abcdef"[..];
+  assert_eq!(
+    many_error_1(a),
+    Err(Err::Error(error_position!(a, ErrorKind::Tag)))
+  );
+  
+  
+  fn many_error(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    many(0.., tag(""))(i)
+  }
+  
+  let a = &b"abcdef"[..];
+  assert_eq!(
+    many_error(a),
+    Err(Err::Error(error_position!(
+      a,
+      ErrorKind::Many
+    )))
+  );
+  
+  
+  fn many_invalid(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    many(2..=1, tag("a"))(i)
+  }
+  
+  let a = &b"a"[..];
+  let b = &b"b"[..];
+  assert_eq!(many_invalid(a), Err(Err::Failure(error_position!(a, ErrorKind::Many))));
+  assert_eq!(many_invalid(b), Err(Err::Failure(error_position!(b, ErrorKind::Many))));
+  
+  
+  fn many_any(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    many(0.., tag("abcd"))(i)
+  }
+  
+  let a = &b"abcdef"[..];
+  let b = &b"abcdabcdefgh"[..];
+  let c = &b"azerty"[..];
+  let d = &b"abcdab"[..];
+  let e = &b"abcd"[..];
+  let f = &b""[..];
+  assert_eq!(many_any(a), Ok((&b"ef"[..], vec![&b"abcd"[..]])));
+  assert_eq!(
+    many_any(b),
+    Ok((&b"efgh"[..], vec![&b"abcd"[..], &b"abcd"[..]]))
+  );
+  assert_eq!(many_any(c), Ok((&b"azerty"[..], Vec::new())));
+  assert_eq!(many_any(d), Err(Err::Incomplete(Needed::new(2))));
+  assert_eq!(many_any(e), Err(Err::Incomplete(Needed::new(4))));
+  assert_eq!(many_any(f), Err(Err::Incomplete(Needed::new(4))));
+  
+  
+  fn many_1(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    many(1.., tag("abcd"))(i)
+  }
+
+  let a = &b"abcdef"[..];
+  let b = &b"abcdabcdefgh"[..];
+  let c = &b"azerty"[..];
+  let d = &b"abcdab"[..];
+  let res1 = vec![&b"abcd"[..]];
+  assert_eq!(many_1(a), Ok((&b"ef"[..], res1)));
+  let res2 = vec![&b"abcd"[..], &b"abcd"[..]];
+  assert_eq!(many_1(b), Ok((&b"efgh"[..], res2)));
+  assert_eq!(
+    many_1(c),
+    Err(Err::Error(error_position!(c, ErrorKind::Tag)))
+  );
+  assert_eq!(many_1(d), Err(Err::Incomplete(Needed::new(2))));
+  
+  
+  fn many_m_n(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    many(2..=4, tag("Abcd"))(i)
+  }
+
+  let a = &b"Abcdef"[..];
+  let b = &b"AbcdAbcdefgh"[..];
+  let c = &b"AbcdAbcdAbcdAbcdefgh"[..];
+  let d = &b"AbcdAbcdAbcdAbcdAbcdefgh"[..];
+  let e = &b"AbcdAb"[..];
+  assert_eq!(
+    many_m_n(a),
+    Err(Err::Error(error_position!(&b"ef"[..], ErrorKind::Tag)))
+  );
+  let res1 = vec![&b"Abcd"[..], &b"Abcd"[..]];
+  assert_eq!(many_m_n(b), Ok((&b"efgh"[..], res1)));
+  let res2 = vec![&b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..]];
+  assert_eq!(many_m_n(c), Ok((&b"efgh"[..], res2)));
+  let res3 = vec![&b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..]];
+  assert_eq!(many_m_n(d), Ok((&b"Abcdefgh"[..], res3)));
+  assert_eq!(many_m_n(e), Err(Err::Incomplete(Needed::new(2))));
+  
+  
+  fn many_fixed(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    many(2, tag("Abcd"))(i)
+  }
+  
+  let a = &b"Abcdef"[..];
+  let b = &b"AbcdAbcdefgh"[..];
+  let c = &b"AbcdAbcdAbcdAbcdefgh"[..];
+  let d = &b"AbcdAb"[..];
+  assert_eq!(
+    many_fixed(a),
+    Err(Err::Error(error_position!(&b"ef"[..], ErrorKind::Tag)))
+  );
+  let res1 = vec![&b"Abcd"[..], &b"Abcd"[..]];
+  assert_eq!(many_fixed(b), Ok((&b"efgh"[..], res1)));
+  let res2 = vec![&b"Abcd"[..], &b"Abcd"[..]];
+  assert_eq!(many_fixed(c), Ok((&b"AbcdAbcdefgh"[..], res2)));
+  assert_eq!(many_fixed(d), Err(Err::Incomplete(Needed::new(2))));
+  
+  
+  fn multi_exclusive(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    many(0..2, tag("Abcd"))(i)
+  }
+  
+  let a = &b"AbcdAbcdAbcd"[..];
+  let b = &b"AAA"[..];
+  let res1 = vec![&b"Abcd"[..]];
+  assert_eq!(multi_exclusive(a), Ok((&b"AbcdAbcd"[..], res1)));
+  let res2 = vec![];
+  assert_eq!(multi_exclusive(b), Ok((&b"AAA"[..], res2)));
+  
+  
+  fn many_never(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    many(0..=0, tag("A"))(i)
+  }
+  
+  let a = &b"AA"[..];
+  let b = &b"B"[..];
+  assert_eq!(many_never(a), Ok((&b"AA"[..], Vec::new())));
+  assert_eq!(many_never(b), Ok((&b"B"[..], Vec::new())));
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn fold_test() {
+  fn fold_into_vec<T>(mut acc: Vec<T>, item: T) -> Vec<T> {
+    acc.push(item);
+    acc
+  }
+  
+  // should not go into an infinite loop
+  fn fold_error_0(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    fn tst(input: &[u8]) -> IResult<&[u8], &[u8]> {
+      Err(Err::Error(error_position!(input, ErrorKind::Tag)))
+    }
+    fold(0.., tst, Vec::new, fold_into_vec)(i)
+  }
+  
+  let a = &b"abcdef"[..];
+  assert_eq!(fold_error_0(a), Ok((a, Vec::new())));
+  
+  
+  fn fold_error_1(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    fn tst(input: &[u8]) -> IResult<&[u8], &[u8]> {
+      Err(Err::Error(error_position!(input, ErrorKind::Tag)))
+    }
+    fold(1.., tst, Vec::new, fold_into_vec)(i)
+  }
+  
+  let a = &b"abcdef"[..];
+  assert_eq!(
+    fold_error_1(a),
+    Err(Err::Error(error_position!(a, ErrorKind::Tag)))
+  );
+  
+  
+  fn fold_error(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    fold(0.., tag(""), Vec::new, fold_into_vec)(i)
+  }
+  
+  let a = &b"abcdef"[..];
+  assert_eq!(
+    fold_error(a),
+    Err(Err::Error(error_position!(
+      a,
+      ErrorKind::Fold
+    )))
+  );
+  
+  
+  fn fold_invalid(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    fold(2..=1, tag("a"), Vec::new, fold_into_vec)(i)
+  }
+  
+  let a = &b"a"[..];
+  let b = &b"b"[..];
+  assert_eq!(fold_invalid(a), Err(Err::Failure(error_position!(a, ErrorKind::Fold))));
+  assert_eq!(fold_invalid(b), Err(Err::Failure(error_position!(b, ErrorKind::Fold))));
+  
+  
+  fn fold_any(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    fold(0.., tag("abcd"), Vec::new, fold_into_vec)(i)
+  }
+  
+  let a = &b"abcdef"[..];
+  let b = &b"abcdabcdefgh"[..];
+  let c = &b"azerty"[..];
+  let d = &b"abcdab"[..];
+  let e = &b"abcd"[..];
+  let f = &b""[..];
+  assert_eq!(fold_any(a), Ok((&b"ef"[..], vec![&b"abcd"[..]])));
+  assert_eq!(
+    fold_any(b),
+    Ok((&b"efgh"[..], vec![&b"abcd"[..], &b"abcd"[..]]))
+  );
+  assert_eq!(fold_any(c), Ok((c, Vec::new())));
+  assert_eq!(fold_any(d), Err(Err::Incomplete(Needed::new(2))));
+  assert_eq!(fold_any(e), Err(Err::Incomplete(Needed::new(4))));
+  assert_eq!(fold_any(f), Err(Err::Incomplete(Needed::new(4))));
+  
+  
+  fn fold_1(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    fold(1.., tag("abcd"), Vec::new, fold_into_vec)(i)
+  }
+
+  let a = &b"abcdef"[..];
+  let b = &b"abcdabcdefgh"[..];
+  let c = &b"azerty"[..];
+  let d = &b"abcdab"[..];
+  let res1 = vec![&b"abcd"[..]];
+  assert_eq!(fold_1(a), Ok((&b"ef"[..], res1)));
+  let res2 = vec![&b"abcd"[..], &b"abcd"[..]];
+  assert_eq!(fold_1(b), Ok((&b"efgh"[..], res2)));
+  assert_eq!(
+    fold_1(c),
+    Err(Err::Error(error_position!(c, ErrorKind::Tag)))
+  );
+  assert_eq!(fold_1(d), Err(Err::Incomplete(Needed::new(2))));
+  
+  
+  fn fold_m_n(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    fold(2..=4, tag("Abcd"), Vec::new, fold_into_vec)(i)
+  }
+
+  let a = &b"Abcdef"[..];
+  let b = &b"AbcdAbcdefgh"[..];
+  let c = &b"AbcdAbcdAbcdAbcdefgh"[..];
+  let d = &b"AbcdAbcdAbcdAbcdAbcdefgh"[..];
+  let e = &b"AbcdAb"[..];
+  assert_eq!(
+    fold_m_n(a),
+    Err(Err::Error(error_position!(&b"ef"[..], ErrorKind::Tag)))
+  );
+  let res1 = vec![&b"Abcd"[..], &b"Abcd"[..]];
+  assert_eq!(fold_m_n(b), Ok((&b"efgh"[..], res1)));
+  let res2 = vec![&b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..]];
+  assert_eq!(fold_m_n(c), Ok((&b"efgh"[..], res2)));
+  let res3 = vec![&b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..], &b"Abcd"[..]];
+  assert_eq!(fold_m_n(d), Ok((&b"Abcdefgh"[..], res3)));
+  assert_eq!(fold_m_n(e), Err(Err::Incomplete(Needed::new(2))));
+  
+  
+  fn fold_fixed(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    fold(2, tag("Abcd"), Vec::new, fold_into_vec)(i)
+  }
+  
+  let a = &b"Abcdef"[..];
+  let b = &b"AbcdAbcdefgh"[..];
+  let c = &b"AbcdAbcdAbcdAbcdefgh"[..];
+  let d = &b"AbcdAb"[..];
+  assert_eq!(
+    fold_fixed(a),
+    Err(Err::Error(error_position!(&b"ef"[..], ErrorKind::Tag)))
+  );
+  let res1 = vec![&b"Abcd"[..], &b"Abcd"[..]];
+  assert_eq!(fold_fixed(b), Ok((&b"efgh"[..], res1)));
+  let res2 = vec![&b"Abcd"[..], &b"Abcd"[..]];
+  assert_eq!(fold_fixed(c), Ok((&b"AbcdAbcdefgh"[..], res2)));
+  assert_eq!(fold_fixed(d), Err(Err::Incomplete(Needed::new(2))));
+  
+  
+  fn fold_exclusive(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    fold(0..2, tag("Abcd"), Vec::new, fold_into_vec)(i)
+  }
+  
+  let a = &b"AbcdAbcdAbcd"[..];
+  let b = &b"AAA"[..];
+  let res1 = vec![&b"Abcd"[..]];
+  assert_eq!(fold_exclusive(a), Ok((&b"AbcdAbcd"[..], res1)));
+  let res2 = vec![];
+  assert_eq!(fold_exclusive(b), Ok((&b"AAA"[..], res2)));
+  
+  
+  fn fold_never(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
+    fold(0..=0, tag("A"), Vec::new, fold_into_vec)(i)
+  }
+  
+  let a = &b"AAA"[..];
+  let b = &b"B"[..];
+  assert_eq!(fold_never(a), Ok((&b"AAA"[..], Vec::new())));
+  assert_eq!(fold_never(b), Ok((&b"B"[..], Vec::new())));
+}
diff --git a/tests/arithmetic.rs b/tests/arithmetic.rs
index 5b627a97a..6f600162e 100644
--- a/tests/arithmetic.rs
+++ b/tests/arithmetic.rs
@@ -4,7 +4,7 @@ use nom::{
   character::complete::char,
   character::complete::{digit1 as digit, space0 as space},
   combinator::map_res,
-  multi::fold_many0,
+  multi::fold,
   sequence::{delimited, pair},
   IResult,
 };
@@ -35,7 +35,8 @@ fn factor(i: &str) -> IResult<&str, i64> {
 fn term(i: &str) -> IResult<&str, i64> {
   let (i, init) = factor(i)?;
 
-  fold_many0(
+  fold(
+    0..,
     pair(alt((char('*'), char('/'))), factor),
     move || init,
     |acc, (op, val): (char, i64)| {
@@ -51,7 +52,8 @@ fn term(i: &str) -> IResult<&str, i64> {
 fn expr(i: &str) -> IResult<&str, i64> {
   let (i, init) = term(i)?;
 
-  fold_many0(
+  fold(
+    0..,
     pair(alt((char('+'), char('-'))), term),
     move || init,
     |acc, (op, val): (char, i64)| {
diff --git a/tests/arithmetic_ast.rs b/tests/arithmetic_ast.rs
index ca1511096..b2e67450a 100644
--- a/tests/arithmetic_ast.rs
+++ b/tests/arithmetic_ast.rs
@@ -8,7 +8,7 @@ use nom::{
   bytes::complete::tag,
   character::complete::{digit1 as digit, multispace0 as multispace},
   combinator::{map, map_res},
-  multi::many0,
+  multi::many,
   sequence::{delimited, preceded},
   IResult,
 };
@@ -90,7 +90,7 @@ fn fold_exprs(initial: Expr, remainder: Vec<(Oper, Expr)>) -> Expr {
 
 fn term(i: &str) -> IResult<&str, Expr> {
   let (i, initial) = factor(i)?;
-  let (i, remainder) = many0(alt((
+  let (i, remainder) = many(0.., alt((
     |i| {
       let (i, mul) = preceded(tag("*"), factor)(i)?;
       Ok((i, (Oper::Mul, mul)))
@@ -106,7 +106,7 @@ fn term(i: &str) -> IResult<&str, Expr> {
 
 fn expr(i: &str) -> IResult<&str, Expr> {
   let (i, initial) = term(i)?;
-  let (i, remainder) = many0(alt((
+  let (i, remainder) = many(0.., alt((
     |i| {
       let (i, add) = preceded(tag("+"), term)(i)?;
       Ok((i, (Oper::Add, add)))
diff --git a/tests/fnmut.rs b/tests/fnmut.rs
index b1486cbe6..995faefdf 100644
--- a/tests/fnmut.rs
+++ b/tests/fnmut.rs
@@ -1,6 +1,6 @@
 use nom::{
   bytes::complete::tag,
-  multi::{many0, many0_count},
+  multi::{many, many0_count},
 };
 
 #[test]
@@ -8,7 +8,7 @@ fn parse() {
   let mut counter = 0;
 
   let res = {
-    let mut parser = many0::<_, _, (), _>(|i| {
+    let mut parser = many::<_, _, (), _, _>(0.., |i| {
       counter += 1;
       tag("abc")(i)
     });
diff --git a/tests/ini.rs b/tests/ini.rs
index e89afe368..4bd06085f 100644
--- a/tests/ini.rs
+++ b/tests/ini.rs
@@ -4,7 +4,7 @@ use nom::{
     alphanumeric1 as alphanumeric, char, multispace0 as multispace, space0 as space,
   },
   combinator::{map, map_res, opt},
-  multi::many0,
+  multi::many,
   sequence::{delimited, pair, separated_pair, terminated},
   IResult, Parser,
 };
@@ -28,7 +28,7 @@ fn key_value(i: &[u8]) -> IResult<&[u8], (&str, &str)> {
 }
 
 fn keys_and_values(i: &[u8]) -> IResult<&[u8], HashMap<&str, &str>> {
-  map(many0(terminated(key_value, opt(multispace))), |vec| {
+  map(many(0.., terminated(key_value, opt(multispace))), |vec| {
     vec.into_iter().collect()
   })(i)
 }
@@ -41,14 +41,17 @@ fn category_and_keys(i: &[u8]) -> IResult<&[u8], (&str, HashMap<&str, &str>)> {
 
 fn categories(i: &[u8]) -> IResult<&[u8], HashMap<&str, HashMap<&str, &str>>> {
   map(
-    many0(separated_pair(
-      category,
-      opt(multispace),
-      map(
-        many0(terminated(key_value, opt(multispace))),
-        |vec: Vec<_>| vec.into_iter().collect(),
+    many(
+      0..,
+      separated_pair(
+        category,
+        opt(multispace),
+        map(
+          many(0.., terminated(key_value, opt(multispace))),
+          |vec: Vec<_>| vec.into_iter().collect(),
+        ),
       ),
-    )),
+    ),
     |vec: Vec<_>| vec.into_iter().collect(),
   )(i)
 }
diff --git a/tests/ini_str.rs b/tests/ini_str.rs
index fb420cdfc..c2998adec 100644
--- a/tests/ini_str.rs
+++ b/tests/ini_str.rs
@@ -2,7 +2,7 @@ use nom::{
   bytes::complete::{is_a, tag, take_till, take_while},
   character::complete::{alphanumeric1 as alphanumeric, char, space0 as space},
   combinator::opt,
-  multi::many0,
+  multi::many,
   sequence::{delimited, pair, terminated},
   IResult, Parser,
 };
@@ -40,7 +40,7 @@ fn key_value(i: &str) -> IResult<&str, (&str, &str)> {
 }
 
 fn keys_and_values_aggregator(i: &str) -> IResult<&str, Vec<(&str, &str)>> {
-  many0(key_value)(i)
+  many(0.., key_value)(i)
 }
 
 fn keys_and_values(input: &str) -> IResult<&str, HashMap<&str, &str>> {
@@ -56,7 +56,7 @@ fn category_and_keys(i: &str) -> IResult<&str, (&str, HashMap<&str, &str>)> {
 
 #[allow(clippy::type_complexity)]
 fn categories_aggregator(i: &str) -> IResult<&str, Vec<(&str, HashMap<&str, &str>)>> {
-  many0(category_and_keys)(i)
+  many(0.., category_and_keys)(i)
 }
 
 fn categories(input: &str) -> IResult<&str, HashMap<&str, HashMap<&str, &str>>> {
diff --git a/tests/issues.rs b/tests/issues.rs
index 0de6b42a8..eca53b583 100644
--- a/tests/issues.rs
+++ b/tests/issues.rs
@@ -24,13 +24,13 @@ mod parse_int {
   use nom::{
     character::streaming::{digit1 as digit, space1 as space},
     combinator::{complete, map, opt},
-    multi::many0,
+    multi::many,
     IResult,
   };
   use std::str;
 
   fn parse_ints(input: &[u8]) -> IResult<&[u8], Vec<i32>> {
-    many0(spaces_or_int)(input)
+    many(0.., spaces_or_int)(input)
   }
 
   fn spaces_or_int(input: &[u8]) -> IResult<&[u8], i32> {
@@ -171,9 +171,9 @@ fn issue_942() {
 #[test]
 fn issue_many_m_n_with_zeros() {
   use nom::character::complete::char;
-  use nom::multi::many_m_n;
-  let mut parser = many_m_n::<_, _, (), _>(0, 0, char('a'));
-  assert_eq!(parser("aaa"), Ok(("aaa", vec![])));
+  use nom::multi::many;
+  let mut parser = many::<_, _, (), _, _>(0..=0, char('a'));
+  assert_eq!(parser("aaa"), Ok(("aaa", vec!())));
 }
 
 #[test]
diff --git a/tests/json.rs b/tests/json.rs
index e8a06fd77..417fe4bbc 100644
--- a/tests/json.rs
+++ b/tests/json.rs
@@ -6,7 +6,7 @@ use nom::{
   character::complete::{anychar, char, multispace0, none_of},
   combinator::{map, map_opt, map_res, value, verify},
   error::ParseError,
-  multi::{fold_many0, separated_list0},
+  multi::{fold, separated_list0},
   number::complete::double,
   sequence::{delimited, preceded, separated_pair},
   IResult, Parser,
@@ -82,7 +82,7 @@ fn character(input: &str) -> IResult<&str, char> {
 fn string(input: &str) -> IResult<&str, String> {
   delimited(
     char('"'),
-    fold_many0(character, String::new, |mut string, c| {
+    fold(0.., character, String::new, |mut string, c| {
       string.push(c);
       string
     }),
diff --git a/tests/mp4.rs b/tests/mp4.rs
index 79dbc7c65..2da6fc1f8 100644
--- a/tests/mp4.rs
+++ b/tests/mp4.rs
@@ -5,7 +5,7 @@ use nom::{
   bytes::streaming::{tag, take},
   combinator::{map, map_res},
   error::ErrorKind,
-  multi::many0,
+  multi::many,
   number::streaming::{be_f32, be_u16, be_u32, be_u64},
   Err, IResult, Needed,
 };
@@ -250,7 +250,7 @@ fn brand_name(input: &[u8]) -> IResult<&[u8], &str> {
 fn filetype_parser(input: &[u8]) -> IResult<&[u8], FileType<'_>> {
   let (i, name) = brand_name(input)?;
   let (i, version) = take(4_usize)(i)?;
-  let (i, brands) = many0(brand_name)(i)?;
+  let (i, brands) = many(0.., brand_name)(i)?;
 
   let ft = FileType {
     major_brand: name,
diff --git a/tests/multiline.rs b/tests/multiline.rs
index 7378b9e3b..b5d3c1f32 100644
--- a/tests/multiline.rs
+++ b/tests/multiline.rs
@@ -1,6 +1,6 @@
 use nom::{
   character::complete::{alphanumeric1 as alphanumeric, line_ending as eol},
-  multi::many0,
+  multi::many,
   sequence::terminated,
   IResult,
 };
@@ -18,7 +18,7 @@ pub fn read_line(input: &str) -> IResult<&str, &str> {
 }
 
 pub fn read_lines(input: &str) -> IResult<&str, Vec<&str>> {
-  many0(read_line)(input)
+  many(0.., read_line)(input)
 }
 
 #[cfg(feature = "alloc")]
diff --git a/tests/overflow.rs b/tests/overflow.rs
index 6f3512aa6..f23d62e50 100644
--- a/tests/overflow.rs
+++ b/tests/overflow.rs
@@ -3,7 +3,7 @@
 
 use nom::bytes::streaming::take;
 #[cfg(feature = "alloc")]
-use nom::multi::{length_data, many0};
+use nom::multi::{length_data, many};
 #[cfg(feature = "alloc")]
 use nom::number::streaming::be_u64;
 use nom::{Err, IResult, Needed, Parser};
@@ -27,7 +27,7 @@ fn overflow_incomplete_tuple() {
 #[cfg(feature = "alloc")]
 fn overflow_incomplete_length_bytes() {
   fn multi(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
-    many0(length_data(be_u64))(i)
+    many(0.., length_data(be_u64))(i)
   }
 
   // Trigger an overflow in length_data
@@ -41,7 +41,7 @@ fn overflow_incomplete_length_bytes() {
 #[cfg(feature = "alloc")]
 fn overflow_incomplete_many0() {
   fn multi(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
-    many0(length_data(be_u64))(i)
+    many(0.., length_data(be_u64))(i)
   }
 
   // Trigger an overflow in many0
@@ -54,10 +54,8 @@ fn overflow_incomplete_many0() {
 #[test]
 #[cfg(feature = "alloc")]
 fn overflow_incomplete_many1() {
-  use nom::multi::many1;
-
   fn multi(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
-    many1(length_data(be_u64))(i)
+    many(1.., length_data(be_u64))(i)
   }
 
   // Trigger an overflow in many1
@@ -87,10 +85,8 @@ fn overflow_incomplete_many_till() {
 #[test]
 #[cfg(feature = "alloc")]
 fn overflow_incomplete_many_m_n() {
-  use nom::multi::many_m_n;
-
   fn multi(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
-    many_m_n(2, 4, length_data(be_u64))(i)
+    many(2..=4, length_data(be_u64))(i)
   }
 
   // Trigger an overflow in many_m_n
@@ -135,7 +131,7 @@ fn overflow_incomplete_length_count() {
 #[cfg(feature = "alloc")]
 fn overflow_incomplete_length_data() {
   fn multi(i: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
-    many0(length_data(be_u64))(i)
+    many(0.., length_data(be_u64))(i)
   }
 
   assert_eq!(
diff --git a/tests/reborrow_fold.rs b/tests/reborrow_fold.rs
index ba3df092d..33b1b7f3a 100644
--- a/tests/reborrow_fold.rs
+++ b/tests/reborrow_fold.rs
@@ -6,7 +6,7 @@ use std::str;
 use nom::bytes::complete::is_not;
 use nom::character::complete::char;
 use nom::combinator::{map, map_res};
-use nom::multi::fold_many0;
+use nom::multi::fold;
 use nom::sequence::delimited;
 use nom::IResult;
 
@@ -23,7 +23,7 @@ fn atom(_tomb: &mut ()) -> impl for<'a> FnMut(&'a [u8]) -> IResult<&'a [u8], Str
 fn list<'a>(i: &'a [u8], tomb: &mut ()) -> IResult<&'a [u8], String> {
   delimited(
     char('('),
-    fold_many0(atom(tomb), String::new, |acc: String, next: String| {
+    fold(0.., atom(tomb), String::new, |acc: String, next: String| {
       acc + next.as_str()
     }),
     char(')'),