1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
pub trait StrExt { fn char_at(&self, idx: usize) -> Option<char>; fn previous(&self, idx: usize) -> Option<char>; } impl StrExt for str { fn char_at(&self, at: usize) -> Option<char> { self.char_indices() .skip_while(|&(idx, _c)| idx < at) .filter_map(|(idx, c)| if idx == at { Some(c) } else { None }) .next() } fn previous(&self, current: usize) -> Option<char> { self.char_indices() .skip_while(|&(idx, c)| idx + c.len_utf8() < current) .filter_map(|(idx, c)| { if idx + c.len_utf8() == current { Some(c) } else { None } }) .next() } } #[cfg(test)] mod test { use super::*; #[test] fn various_len() { let data = "абвГДеёlжз"; assert_eq!(Some('а'), data.char_at(0)); assert_eq!(Some('б'), data.char_at(2)); assert_eq!(Some('в'), data.char_at(4)); assert_eq!(Some('Г'), data.char_at(6)); assert_eq!(Some('Д'), data.char_at(8)); assert_eq!(Some('е'), data.char_at(10)); assert_eq!(Some('ё'), data.char_at(12)); assert_eq!(Some('l'), data.char_at(14)); assert_eq!(Some('ж'), data.char_at(15)); assert_eq!(Some('з'), data.char_at(17)); assert_eq!(None, data.previous(0)); assert_eq!(Some('а'), data.previous(2)); assert_eq!(Some('б'), data.previous(4)); assert_eq!(Some('в'), data.previous(6)); assert_eq!(Some('Г'), data.previous(8)); assert_eq!(Some('Д'), data.previous(10)); assert_eq!(Some('е'), data.previous(12)); assert_eq!(Some('ё'), data.previous(14)); assert_eq!(Some('l'), data.previous(15)); assert_eq!(Some('ж'), data.previous(17)); } #[test] fn boundary() { let data = "ф"; assert_eq!(None, data.char_at(1)); } }