Better string manipulation

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.

fn extract_delimited_with_explicit_splits<'src>(
    text: &'src str,
    delimiter: &str,
) -> Option<&'src str> {
    let (_head, rest) = text.split_once(delimiter)?;
    let (target, _tail) = rest.split_once(delimiter)?;
    Some(target)
}

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 extract_delimited<'src>(
  text: &'src str, 
  delimiter: &str,
) -> Option<&'src str> {
    text.split(delimiter).nth(1)
}
9 Likes

Note that libc is crate that gives you access to C library. You probably meant std?

I don't see it that way. What would happen if string is not found in C#?

Anyway, the more-or-less direct translation of your code would look like this:

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);
}

4 Likes

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.

Why do you want to use libc when Rust already provides higher-level, safe, fast, and Unicode-aware functions?

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.

which incosistencies specifically?

Well if you want to use rust you should learn rust, not try to write C in rust.

3 Likes

Sure, this is what I'm trying to do. I already have a good solution using pure Rust, so I don't bother with libc.