Created
April 22, 2020 22:08
-
-
Save jonhoo/2a7fdcf79be03e51a5f95cd326f2a1e8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![warn(rust_2018_idioms)] | |
#[derive(Debug)] | |
pub struct StrSplit<'haystack, D> { | |
remainder: Option<&'haystack str>, | |
delimiter: D, | |
} | |
impl<'haystack, D> StrSplit<'haystack, D> { | |
pub fn new(haystack: &'haystack str, delimiter: D) -> Self { | |
Self { | |
remainder: Some(haystack), | |
delimiter, | |
} | |
} | |
} | |
pub trait Delimiter { | |
fn find_next(&self, s: &str) -> Option<(usize, usize)>; | |
} | |
impl<'haystack, D> Iterator for StrSplit<'haystack, D> | |
where | |
D: Delimiter, | |
{ | |
type Item = &'haystack str; | |
fn next(&mut self) -> Option<Self::Item> { | |
let remainder = self.remainder.as_mut()?; | |
if let Some((delim_start, delim_end)) = self.delimiter.find_next(remainder) { | |
let until_delimiter = &remainder[..delim_start]; | |
*remainder = &remainder[delim_end..]; | |
Some(until_delimiter) | |
} else { | |
self.remainder.take() | |
} | |
} | |
} | |
impl Delimiter for &str { | |
fn find_next(&self, s: &str) -> Option<(usize, usize)> { | |
s.find(self).map(|start| (start, start + self.len())) | |
} | |
} | |
impl Delimiter for char { | |
fn find_next(&self, s: &str) -> Option<(usize, usize)> { | |
s.char_indices() | |
.find(|(_, c)| c == self) | |
.map(|(start, _)| (start, start + self.len_utf8())) | |
} | |
} | |
pub fn until_char(s: &str, c: char) -> &'_ str { | |
StrSplit::new(s, c) | |
.next() | |
.expect("StrSplit always gives at least one result") | |
} | |
#[test] | |
fn until_char_test() { | |
assert_eq!(until_char("hello world", 'o'), "hell"); | |
} | |
#[test] | |
fn it_works() { | |
let haystack = "a b c d e"; | |
let letters: Vec<_> = StrSplit::new(haystack, " ").collect(); | |
assert_eq!(letters, vec!["a", "b", "c", "d", "e"]); | |
} | |
#[test] | |
fn tail() { | |
let haystack = "a b c d "; | |
let letters: Vec<_> = StrSplit::new(haystack, " ").collect(); | |
assert_eq!(letters, vec!["a", "b", "c", "d", ""]); | |
} |
Hi @jonhoo,
What is the difference between these two approach in next method
fn next(&mut self) -> Option<Self::Item> {
// if let Some(ref mut remainder) = self.remainder {
// if let Some(next_delim) = remainder.find(self.delimeter) {
// let until_delimeter = &remainder[..next_delim];
// *remainder = &remainder[(next_delim + self.delimeter.len())..];
// Some(until_delimeter)
// } else {
// self.remainder.take()
// }
// } else {
// None
// }
if let Some(remainder) = self.remainder {
if let Some(next_delim) = remainder.find(self.delimeter) {
let until_delimeter = &remainder[..next_delim];
self.remainder = Some(&remainder[(next_delim + self.delimeter.len())..]);
Some(until_delimeter)
} else {
self.remainder.take()
}
} else {
None
}
}
The compiler doesn't give any error and work as expected. Is it less efficient as I'm moving out and then reassigning, or some rust idiomatic I'm missing?
@MaxAsif Either of those are fine. They both work here because the type inside the Option
in self.remainder
is Copy
, and so therefore you can pull it out in the initial if let Some
and still be allowed to overwrite it further down. That wouldn't be the case if the type wasn't Copy
(e.g., if it were a String
). In that case you'd have to use the former (i.e., the commented-out code).
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
thanks, as a rust beginner, is something confusing to spot whether a type implemnent copy trait or not. Thanks for making the one the best rust content on Youtube!!