I'm converting a C# program to Rust and have reached the string manipulation stage. The code below finds the first two occurrences of string search in string full and extracts the characters in between.
fn main() {
let full = "asdjkABC123abcekldfhXYZ456ABCfgasdgfABCasdgfasf";
let search = "ABC";
//C#: int first = full.IndexOf(search) + search.Length;
let first;
match full.find(search) {
None => return,
Some(xx) => first = xx + search.len(),
}
//C#: int second = full.IndexOf(search, first);
let part;
unsafe { part = full.get_unchecked(first..full.len()); }
let second;
match part.find(search) {
None => return,
Some(xx) => second = xx,
}
//C#: string result = full.Substring(first, second-first);
let result;
unsafe { result = part.get_unchecked(0..second); }
println!("{}", result);
}
I've implemented this using the String functions. But I would appreciate an equivalent implementation with libc. I haven't found any good examples of libc and have found it difficult to use.
I guess that using regex could provide an elegant solution. Perhaps someone would feel like showing it.
The program above is only the tip of the iceberg, and I haven't decided yet what technique to use. One observation is that C# seems very concise compared to Rust.
That's because libc is a library for C programs, and strings are very much second-class citizens in C.
I think that might be because your Rust version isn't very idiomatic.
If it were me, I'd use the split_once() method to split the string into the bits before and after your delimiter. There's no need to mess around with indices.
Or you can make the code even cleaner by doing string.split() and taking the second element. Unlike C#, splitting won't create a temporary array to store the pieces in, and splitting is done lazily (i.e. we don't look for the next occurrence until you call the iterator's next() method.
fn main() {
let full = "asdjkABC123abcekldfhXYZ456ABCfgasdgfABCasdgfasf";
let search = "ABC";
//C#: int first = full.IndexOf(search) + search.Length;
let first = full.find(search).unwrap() + search.len();
//C#: int second = full.IndexOf(search, first);
let second = &full[first..].find(search).unwrap()+first;
//C#: string result = full.Substring(first, second-first);
let result = &full[first..second];
println!("{}", result);
}
Thank you. Okey, appending unwrap() to Option<usize> changes the type to usize; I didn't know that. "What would happen if string is not found in C#?" is a relevant question, but it is solved with if(index<0) return; which is still concise.
I did mean the libc crate as an alternative, but the inconsistencies with the Rust types discouraged me.
Well, using split has proven to be a terrific idea, thank you. I simply use str.split().
fn main() {
let full = "asdjkABC123abcekldfhXYZ456ABCfgasdgfABCasdgfasf";
let search = "ABC";
let v: Vec<&str> = full.split(search).collect();
let result = v[1];
println!("{}", result);
}
I love this solution. I could think about going back to C# and implementing it there as well. In reality, I need both v[1] and v[2], and I get both of them right away.
This is because I have decades of experience with C, C++, Java, and C# but zero experience with Rust. But I've already found a very good solution with str, and I forget about libc.