Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement String::remove_matches #71780

Merged
merged 1 commit into from
Mar 19, 2021

Conversation

jcotton42
Copy link
Contributor

Closes #50206.

I lifted the function help from @frewsxcv's original PR (#50015), hope they don't mind.

I'm also wondering whether it would be useful for remove_matches to collect up the removed substrings into a Vec and return them, right now they're just overwritten by the copy and lost.

@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @Mark-Simulacrum (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 1, 2020
// the Searcher docs
unsafe {
for (start, end) in matches {
ptr::copy(
Copy link
Contributor Author

@jcotton42 jcotton42 May 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't use copy_nonoverlapping here right? That was my intuition, but I just wanted to check it.

Copy link
Contributor

@pickfire pickfire Sep 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird, I thought it does not overlap? The memory for deletion should not overlap to begin with. So I think it is good to use copy_nonoverlapping.

By the way, I think it may be better to keep a reference to current cursor rather than keeping shrunk_by which we can directly work on. It could probably reduce one add operation here.

src/liballoc/string.rs Outdated Show resolved Hide resolved
@frewsxcv frewsxcv requested a review from a team May 2, 2020 12:50
@frewsxcv frewsxcv added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label May 2, 2020
@rust-highfive
Copy link
Collaborator

Your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
========================== Starting Command Output ===========================
[command]/bin/bash --noprofile --norc /home/vsts/work/_temp/bc743f0a-b1be-4350-b56c-236304befd38.sh

##[section]Finishing: Disable git automatic line ending conversion
##[section]Starting: Checkout rust-lang/rust@refs/pull/71780/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
---
##[command]git remote add origin https://github.com/rust-lang/rust
##[command]git config gc.auto 0
##[command]git config --get-all http.https://github.com/rust-lang/rust.extraheader
##[command]git config --get-all http.proxy
##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/71780/merge:refs/remotes/pull/71780/merge
---
tar: Error is not recoverable: exiting now

##[error]Bash exited with code '2'.
##[section]Finishing: Install awscli
##[section]Starting: Checkout rust-lang/rust@refs/pull/71780/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
Author       : Microsoft
Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
==============================================================================
Cleaning any cached credential from repository: rust-lang/rust (GitHub)
##[section]Finishing: Checkout rust-lang/rust@refs/pull/71780/merge to s
Cleaning up task key
Start cleaning up orphan processes.
Terminate orphan process: pid (3605) (python)
##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

@Mark-Simulacrum
Copy link
Member

Let's try r? @kennytm who I believe is fairly familiar with the searcher API and such


let matches = {
let mut searcher = pat.into_searcher(self);
let mut matches = Vec::new();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... I don't think using a Vec here counts as "does not use any allocation" (the original promise in #50206).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally I couldn't think of a way to do this without allocating a Vec to hold the match start/ends, but I think I have an idea now that may work. I'll try it when I get off work today in about 6 hours.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went ahead and tested my idea and unfortunately it doesn't work. The way I can see this you either have to:
a. Store the match start/end pairs somewhere, then start modifying the string, as I've done here
or
b. Modify the string during searching.

The problem with b is that the searcher will end up missing matches as parts it would've matched on are potentially moved behind where it's looking.

Here's the code I tried, for reference:

use core::str::pattern::Searcher;
let vec_ptr = self.vec.as_mut_ptr();

let len = self.len();
let mut shrunk_by = 0;
let mut searcher = pat.into_searcher(self);

// SAFETY: start and end will be on utf8 byte boundaries per
// the Searcher docs
unsafe {
    while let Some((start, end)) = searcher.next_match() {
        ptr::copy(
            vec_ptr.add(end - shrunk_by),
            vec_ptr.add(start - shrunk_by),
            len - end,
        );
        shrunk_by += end - start;
    }
    drop(searcher);
    self.vec.set_len(len - shrunk_by);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see value in this new method, even if it does allocate. i'm not on the libs team so don't weight my opinion too heavily, but my vote would be to merge a working implementation, and then open up a follow up issue for removing the allocation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do think this method is better, I don't see why do we need another Vec for this. The only good reason I see doing a two-pass method like here is used in SIMD JSON which the first pass identify the points which makes deserialization faster.

@jcotton42
Copy link
Contributor Author

By the way since we've gotten this far should I open a tracking issue and update the unstable attribute accordingly?

@Elinvynia Elinvynia added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 20, 2020
@Elinvynia Elinvynia added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 27, 2020
@dtolnay dtolnay removed the request for review from a team May 28, 2020 06:51
#[unstable(feature = "string_remove_matches", reason = "new API", issue = "none")]
pub fn remove_matches<'a, P>(&'a mut self, pat: P)
where
P: for<'x> Pattern<'x>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bound is pretty unusual. Does something block this from being implementable with only a P: Pattern<'a> bound?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wouldn't compile without the higher-rank trait bound. I talked to someone on the community Discord about this and they suggested the HRTB and it made it compile.

I think it had something to do with not having the HRTB meant Rust thought pat was going to live as long as self was, when in fact the lifetime of pat had to be shorter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I change P: for<'x> Pattern<'x> to P: Patten<'a> compiling fails with:

error[E0502]: cannot borrow `self.vec` as mutable because it is also borrowed as immutable
    --> src/liballoc/string.rs:1274:21
     |
1249 |     pub fn remove_matches<'a, P>(&'a mut self, pat: P)
     |                           -- lifetime `'a` defined here
...
1256 |             let mut searcher = pat.into_searcher(self);
     |                                -----------------------
     |                                |                 |
     |                                |                 immutable borrow occurs here
     |                                argument requires that `*self` is borrowed for `'a`
...
1274 |                     self.vec.as_mut_ptr().add(end - shrunk_by),
     |                     ^^^^^^^^ mutable borrow occurs here

error[E0502]: cannot borrow `self.vec` as mutable because it is also borrowed as immutable
    --> src/liballoc/string.rs:1275:21
     |
1249 |     pub fn remove_matches<'a, P>(&'a mut self, pat: P)
     |                           -- lifetime `'a` defined here
...
1256 |             let mut searcher = pat.into_searcher(self);
     |                                -----------------------
     |                                |                 |
     |                                |                 immutable borrow occurs here
     |                                argument requires that `*self` is borrowed for `'a`
...
1275 |                     self.vec.as_mut_ptr().add(start - shrunk_by),
     |                     ^^^^^^^^ mutable borrow occurs here

error[E0502]: cannot borrow `self.vec` as mutable because it is also borrowed as immutable
    --> src/liballoc/string.rs:1280:13
     |
1249 |     pub fn remove_matches<'a, P>(&'a mut self, pat: P)
     |                           -- lifetime `'a` defined here
...
1256 |             let mut searcher = pat.into_searcher(self);
     |                                -----------------------
     |                                |                 |
     |                                |                 immutable borrow occurs here
     |                                argument requires that `*self` is borrowed for `'a`
...
1280 |             self.vec.set_len(len - shrunk_by);
     |             ^^^^^^^^ mutable borrow occurs here

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0502`.
error: could not compile `alloc`.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dtolnay does this answer your question about why I used this trait bound?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not -- it just says that the current implementation relies on the too strict bound. In #71780 (review) I wrote that we'd want to fix the implementation so that a less strict bound is used.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example the following code also requires an unnecessarily strict bound, and does not compile if you remove 'a: 'static, but it's easy to fix the implementation to not require that bound.

pub fn first<'a, T>(input: &'a [T]) -> Option<&'a T>
where
    'a: 'static,
{
    let slice: &'static [T] = input;
    slice.get(0)
}

@dtolnay
Copy link
Member

dtolnay commented May 28, 2020

I am on board with landing this unstable (with an appropriate tracking issue). But I haven't reviewed the implementation at all other than the signature.

Copy link
Member

@dtolnay dtolnay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding #71780 (review): @jcotton42 it would be good to understand the error better. Can you put together a reproduction in the playground by stubbing out a Pattern trait and copying the method body from this PR?

Pattern<'a> is supposed to mean that it can search inside of data with lifetime 'a. Here we have incoming data with lifetime 'a that gets searched inside of, so any stricter trait bound should be fixable by fixing the implementation.

@jcotton42 jcotton42 force-pushed the string_remove_matches branch 2 times, most recently from 77ff362 to 58640a9 Compare June 3, 2020 23:00
@jcotton42
Copy link
Contributor Author

Sorry about the delay on testing the lifetime stuff, I've been having difficulties getting Rust to build on Windows, so I've switched to WSL in the meantime and am currently waiting for it to build so I can reproduce the lifetime error

@joshtriplett
Copy link
Member

Looks good to me.

@bors r+

@bors
Copy link
Contributor

bors commented Mar 18, 2021

📌 Commit a2571cf has been approved by joshtriplett

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 18, 2021
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Mar 18, 2021
…joshtriplett

Implement String::remove_matches

Closes rust-lang#50206.

I lifted the function help from `@frewsxcv's` original PR (rust-lang#50015), hope they don't mind.

I'm also wondering whether it would be useful for `remove_matches` to collect up the removed substrings into a `Vec` and return them, right now they're just overwritten by the copy and lost.
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Mar 18, 2021
…joshtriplett

Implement String::remove_matches

Closes rust-lang#50206.

I lifted the function help from ``@frewsxcv's`` original PR (rust-lang#50015), hope they don't mind.

I'm also wondering whether it would be useful for `remove_matches` to collect up the removed substrings into a `Vec` and return them, right now they're just overwritten by the copy and lost.
@bors
Copy link
Contributor

bors commented Mar 19, 2021

⌛ Testing commit a2571cf with merge b1000219aa1d66a36070a3a334f793692a4c0316...

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Mar 19, 2021
…joshtriplett

Implement String::remove_matches

Closes rust-lang#50206.

I lifted the function help from `@frewsxcv's` original PR (rust-lang#50015), hope they don't mind.

I'm also wondering whether it would be useful for `remove_matches` to collect up the removed substrings into a `Vec` and return them, right now they're just overwritten by the copy and lost.
@Dylan-DPC-zz
Copy link

@bors retry yield (included in rollup)

@rust-log-analyzer
Copy link
Collaborator

A job failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)

@bors
Copy link
Contributor

bors commented Mar 19, 2021

⌛ Testing commit a2571cf with merge eb95ace...

@bors
Copy link
Contributor

bors commented Mar 19, 2021

☀️ Test successful - checks-actions
Approved by: joshtriplett
Pushing eb95ace to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Mar 19, 2021
@bors bors merged commit eb95ace into rust-lang:master Mar 19, 2021
@rustbot rustbot added this to the 1.52.0 milestone Mar 19, 2021
@frewsxcv
Copy link
Member

frewsxcv commented Mar 19, 2021 via email

@joshtriplett
Copy link
Member

It's still unstable, so it's not too late to make changes.

However, this method needs to be on String, because it mutates the string. An analogous method on str would need to make a copy.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jul 31, 2023
…string, r=Mark-Simulacrum

Improve test case for experimental API remove_matches

## Add Test Cases for `remove_matches` Function

### Motivation

After reading the discussion in [this GitHub thread](rust-lang#71780), I'm trying to redesign the current API to use less memory when working with `String` and to make it simpler. I've discovered that some test cases are very helpful in ensuring that the new API behaves as intended. I'm still in the process of redesigning the current API, and these test cases have proven to be very useful.

### Testing

The current test has been tested with the command `./x test --stage 0 library/alloc`.

### Overview

This pull request adds several new test cases for the `remove_matches` function to make sure it works correctly in different situations. The `remove_matches` function is used to get rid of all instances of a specific pattern from a given text. These test cases thoroughly check how the function behaves in various scenarios.

### Test Cases

1. **Single Pattern Occurrence** (`test_single_pattern_occurrence`):
   - Description: Tests the removal of a single pattern occurrence from the text.
   - Input: Text: "abc", Pattern: 'b'
   - Expected Output: "ac"

2. **Repeat Test Single Pattern Occurrence** (`repeat_test_single_pattern_occurrence`):
   - Description: Repeats the previous test case to ensure consecutive removal of the same pattern.
   - Input: Text: "ac", Pattern: 'b'
   - Expected Output: "ac"

3. **Single Character Pattern** (`test_single_character_pattern`):
   - Description: Tests the removal of a single character pattern.
   - Input: Text: "abcb", Pattern: 'b'
   - Expected Output: "ac"

4. **Pattern with Special Characters** (`test_pattern_with_special_characters`):
   - Description: Tests the removal of a pattern containing special characters.
   - Input: Text: "ศไทย中华Việt Nam; foobarศ", Pattern: 'ศ'
   - Expected Output: "ไทย中华Việt Nam; foobar"

5. **Pattern Empty Text and Pattern** (`test_pattern_empty_text_and_pattern`):
   - Description: Tests the removal of an empty pattern from an empty text.
   - Input: Text: "", Pattern: ""
   - Expected Output: ""

6. **Pattern Empty Text** (`test_pattern_empty_text`):
   - Description: Tests the removal of a pattern from an empty text.
   - Input: Text: "", Pattern: "something"
   - Expected Output: ""

7. **Empty Pattern** (`test_empty_pattern`):
   - Description: Tests the behavior of removing an empty pattern from the text.
   - Input: Text: "Testing with empty pattern.", Pattern: ""
   - Expected Output: "Testing with empty pattern."

8. **Multiple Consecutive Patterns 1** (`test_multiple_consecutive_patterns_1`):
   - Description: Tests the removal of multiple consecutive occurrences of a pattern.
   - Input: Text: "aaaaa", Pattern: 'a'
   - Expected Output: ""

9. **Multiple Consecutive Patterns 2** (`test_multiple_consecutive_patterns_2`):
   - Description: Tests the removal of a longer pattern that occurs consecutively.
   - Input: Text: "Hello **world****today!**", Pattern: "**"
   - Expected Output: "Hello worldtoday!"

10. **Case Insensitive Pattern** (`test_case_insensitive_pattern`):
    - Description: Tests the removal of a case-insensitive pattern from the text.
    - Input: Text: "CASE ** SeNsItIvE ** PaTtErN.", Pattern: "sEnSiTiVe"
    - Expected Output: "CASE ** SeNsItIvE ** PaTtErN."
github-actions bot pushed a commit to rust-lang/miri that referenced this pull request Aug 2, 2023
…=Mark-Simulacrum

Improve test case for experimental API remove_matches

## Add Test Cases for `remove_matches` Function

### Motivation

After reading the discussion in [this GitHub thread](rust-lang/rust#71780), I'm trying to redesign the current API to use less memory when working with `String` and to make it simpler. I've discovered that some test cases are very helpful in ensuring that the new API behaves as intended. I'm still in the process of redesigning the current API, and these test cases have proven to be very useful.

### Testing

The current test has been tested with the command `./x test --stage 0 library/alloc`.

### Overview

This pull request adds several new test cases for the `remove_matches` function to make sure it works correctly in different situations. The `remove_matches` function is used to get rid of all instances of a specific pattern from a given text. These test cases thoroughly check how the function behaves in various scenarios.

### Test Cases

1. **Single Pattern Occurrence** (`test_single_pattern_occurrence`):
   - Description: Tests the removal of a single pattern occurrence from the text.
   - Input: Text: "abc", Pattern: 'b'
   - Expected Output: "ac"

2. **Repeat Test Single Pattern Occurrence** (`repeat_test_single_pattern_occurrence`):
   - Description: Repeats the previous test case to ensure consecutive removal of the same pattern.
   - Input: Text: "ac", Pattern: 'b'
   - Expected Output: "ac"

3. **Single Character Pattern** (`test_single_character_pattern`):
   - Description: Tests the removal of a single character pattern.
   - Input: Text: "abcb", Pattern: 'b'
   - Expected Output: "ac"

4. **Pattern with Special Characters** (`test_pattern_with_special_characters`):
   - Description: Tests the removal of a pattern containing special characters.
   - Input: Text: "ศไทย中华Việt Nam; foobarศ", Pattern: 'ศ'
   - Expected Output: "ไทย中华Việt Nam; foobar"

5. **Pattern Empty Text and Pattern** (`test_pattern_empty_text_and_pattern`):
   - Description: Tests the removal of an empty pattern from an empty text.
   - Input: Text: "", Pattern: ""
   - Expected Output: ""

6. **Pattern Empty Text** (`test_pattern_empty_text`):
   - Description: Tests the removal of a pattern from an empty text.
   - Input: Text: "", Pattern: "something"
   - Expected Output: ""

7. **Empty Pattern** (`test_empty_pattern`):
   - Description: Tests the behavior of removing an empty pattern from the text.
   - Input: Text: "Testing with empty pattern.", Pattern: ""
   - Expected Output: "Testing with empty pattern."

8. **Multiple Consecutive Patterns 1** (`test_multiple_consecutive_patterns_1`):
   - Description: Tests the removal of multiple consecutive occurrences of a pattern.
   - Input: Text: "aaaaa", Pattern: 'a'
   - Expected Output: ""

9. **Multiple Consecutive Patterns 2** (`test_multiple_consecutive_patterns_2`):
   - Description: Tests the removal of a longer pattern that occurs consecutively.
   - Input: Text: "Hello **world****today!**", Pattern: "**"
   - Expected Output: "Hello worldtoday!"

10. **Case Insensitive Pattern** (`test_case_insensitive_pattern`):
    - Description: Tests the removal of a case-insensitive pattern from the text.
    - Input: Text: "CASE ** SeNsItIvE ** PaTtErN.", Pattern: "sEnSiTiVe"
    - Expected Output: "CASE ** SeNsItIvE ** PaTtErN."
thomcc pushed a commit to tcdi/postgrestd that referenced this pull request Oct 17, 2023
…=Mark-Simulacrum

Improve test case for experimental API remove_matches

## Add Test Cases for `remove_matches` Function

### Motivation

After reading the discussion in [this GitHub thread](rust-lang/rust#71780), I'm trying to redesign the current API to use less memory when working with `String` and to make it simpler. I've discovered that some test cases are very helpful in ensuring that the new API behaves as intended. I'm still in the process of redesigning the current API, and these test cases have proven to be very useful.

### Testing

The current test has been tested with the command `./x test --stage 0 library/alloc`.

### Overview

This pull request adds several new test cases for the `remove_matches` function to make sure it works correctly in different situations. The `remove_matches` function is used to get rid of all instances of a specific pattern from a given text. These test cases thoroughly check how the function behaves in various scenarios.

### Test Cases

1. **Single Pattern Occurrence** (`test_single_pattern_occurrence`):
   - Description: Tests the removal of a single pattern occurrence from the text.
   - Input: Text: "abc", Pattern: 'b'
   - Expected Output: "ac"

2. **Repeat Test Single Pattern Occurrence** (`repeat_test_single_pattern_occurrence`):
   - Description: Repeats the previous test case to ensure consecutive removal of the same pattern.
   - Input: Text: "ac", Pattern: 'b'
   - Expected Output: "ac"

3. **Single Character Pattern** (`test_single_character_pattern`):
   - Description: Tests the removal of a single character pattern.
   - Input: Text: "abcb", Pattern: 'b'
   - Expected Output: "ac"

4. **Pattern with Special Characters** (`test_pattern_with_special_characters`):
   - Description: Tests the removal of a pattern containing special characters.
   - Input: Text: "ศไทย中华Việt Nam; foobarศ", Pattern: 'ศ'
   - Expected Output: "ไทย中华Việt Nam; foobar"

5. **Pattern Empty Text and Pattern** (`test_pattern_empty_text_and_pattern`):
   - Description: Tests the removal of an empty pattern from an empty text.
   - Input: Text: "", Pattern: ""
   - Expected Output: ""

6. **Pattern Empty Text** (`test_pattern_empty_text`):
   - Description: Tests the removal of a pattern from an empty text.
   - Input: Text: "", Pattern: "something"
   - Expected Output: ""

7. **Empty Pattern** (`test_empty_pattern`):
   - Description: Tests the behavior of removing an empty pattern from the text.
   - Input: Text: "Testing with empty pattern.", Pattern: ""
   - Expected Output: "Testing with empty pattern."

8. **Multiple Consecutive Patterns 1** (`test_multiple_consecutive_patterns_1`):
   - Description: Tests the removal of multiple consecutive occurrences of a pattern.
   - Input: Text: "aaaaa", Pattern: 'a'
   - Expected Output: ""

9. **Multiple Consecutive Patterns 2** (`test_multiple_consecutive_patterns_2`):
   - Description: Tests the removal of a longer pattern that occurs consecutively.
   - Input: Text: "Hello **world****today!**", Pattern: "**"
   - Expected Output: "Hello worldtoday!"

10. **Case Insensitive Pattern** (`test_case_insensitive_pattern`):
    - Description: Tests the removal of a case-insensitive pattern from the text.
    - Input: Text: "CASE ** SeNsItIvE ** PaTtErN.", Pattern: "sEnSiTiVe"
    - Expected Output: "CASE ** SeNsItIvE ** PaTtErN."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Introduce String::remove_matches to remove all matches of a pattern in a string.