HiveBrain v1.2.0
Get Started
← Back to all entries
patternrustMinor

Transposing characters in a string

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
characterstransposingstring

Problem

I want to create a function that takes a string and returns an array of versions of it in which always one pair of characters is transposed (swapped).

I came up with this:

fn transposes(s: &str) -> Vec {
    let mut vec = vec![];

    let b = s.as_bytes();

    for i in 1..b.len() {
        let mut transpose = String::with_capacity(s.len());
        transpose.push_str(&s[..i - 1]);
        transpose.push(s.as_bytes()[i] as char);
        transpose.push(s.as_bytes()[i - 1] as char);
        transpose.push_str(&s[i + 1..]);

        vec.push(transpose);
    }

    vec
}

fn main() {
    let s = "maxi".to_owned();
    println!("{:?}", transposes(&s));   
}


Output is:

["amxi", "mxai", "maix"]


That result is what I want, it does:

  • First swap m and a



  • Then swap a and x



  • Then swap i and x



I don't find the code super readable because of the string slicing going on. In C it would be a mere "pointer swap", but my Rust version looks a bit more involved.

Is there a better way to do what I want?

It is a fair assumption that this will only work correctly for ASCII strings.

Solution


  • If it's just ASCII, treat it as a byte array all the way until the end. Vec has a swap method and you can reconstruct a String from the bytes.



  • Use map and collect instead of allocating a Vec and manually pushing.



  • There's no need to allocate a String to pass to &str.



fn transposes(s: &str) -> Vec {
    let bytes = s.as_bytes();

    (1..bytes.len()).map(|i| {
        let mut transpose = bytes.to_owned();
        transpose.swap(i - 1, i);
        String::from_utf8(transpose).expect("Invalid UTF-8")
    }).collect()
}

fn main() {
    println!("{:?}", transposes("maxi"));
}


Even better, if your data is ASCII, encode that in the type system:

extern crate ascii;

use ascii::{AsciiStr, AsciiString};

fn transposes(s: &AsciiStr) -> Vec {
    (1..s.len()).map(|i| {
        let mut transpose = s.to_owned();
        transpose.as_mut_slice().swap(i - 1, i);
        transpose
    }).collect()
}

fn main() {
    let s = AsciiStr::from_ascii("maxi").expect("Not ASCII");
    println!("{:?}", transposes(s));
}


Note that the transposes function can no longer fail due to invalid strings.

Code Snippets

fn transposes(s: &str) -> Vec<String> {
    let bytes = s.as_bytes();

    (1..bytes.len()).map(|i| {
        let mut transpose = bytes.to_owned();
        transpose.swap(i - 1, i);
        String::from_utf8(transpose).expect("Invalid UTF-8")
    }).collect()
}

fn main() {
    println!("{:?}", transposes("maxi"));
}
extern crate ascii;

use ascii::{AsciiStr, AsciiString};

fn transposes(s: &AsciiStr) -> Vec<AsciiString> {
    (1..s.len()).map(|i| {
        let mut transpose = s.to_owned();
        transpose.as_mut_slice().swap(i - 1, i);
        transpose
    }).collect()
}

fn main() {
    let s = AsciiStr::from_ascii("maxi").expect("Not ASCII");
    println!("{:?}", transposes(s));
}

Context

StackExchange Code Review Q#155294, answer score: 4

Revisions (0)

No revisions yet.