How can I improve my function?

I've written function to push new items or update under given index, can I make it somehow better?

fn insert_or_update<'a>(arr: &mut Vec<&'a str>, idx: usize, val: &'a str) {
    match arr.get(idx) {
        Some(_) => std::mem::replace(&mut arr[idx], val),
        None => {
            arr.push(val);
            ""
        },
    };
}

fn main() {
    let mut arr = vec!["a", "b", "c"];

    insert_or_update(&mut arr, 0, "replaced");
    
    println!("replaced {:?}", &arr);
    
    insert_or_update(&mut arr, 99, "inserted");
    
    println!("inserted {:?}", &arr);
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a5db0a596176fc9d013b186777913ea2

I don't like that I match Some(_) and than replacing the value, also the expected returning type is string so when there is no such item it returns "" but this looks clutter to me

You could use get_mut:

fn insert_or_update<'a>(arr: &mut Vec<&'a str>, idx: usize, val: &'a str) {
    match arr.get_mut(idx) {
        Some(value) => { *value = val; val }
        None => {
            arr.push(val);
            ""
        },
    };
}

You can use get_mut(), and a plain assignment: Rust Playground

fn insert_or_update<T>(arr: &mut Vec<T>, idx: usize, val: T) {
    match arr.get_mut(idx) {
        Some(item) => *item = val,
        None => arr.push(val)
    }
}
5 Likes

I don't like that when you insert the index is not used. It seems a very weird behavior. Is expect that is I called your function with index 99, my string would afterwards appear at index 99.

3 Likes

Well, that's how it is using a Vec :slightly_smiling_face:
If you want to map arbitrary indices to &str, you could use a HashMap.

2 Likes

I agree, but it's a problem with the API. It's combining two operations that don't combine well. Either the user is aware when the index is out of bounds and could more easily not use this function, or the user is not sure if the index is in bounds and therefore might be surprised by the result. I don't see how this function can be both useful and correct.

3 Likes
fn update<T: Clone>(arr: &mut Vec<T>, idx: usize, val: T) {
    match arr.get_mut(idx) {
        Some(item) => *item = val,
        None => arr.resize(idx.checked_add(1).expect("Index too large"), val),
    }
}
// Or something analogous with T: Default

Not that I'm saying this is a good idea...

1 Like

Thanks for your great help, learned much again! It turned out that form my use case I was looking for HashMap :upside_down_face:

use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert("html_cache_0", "a");
    map.insert("html_cache_1", "b");
    map.insert("html_cache_2", "c");

    map.insert("html_cache_0", "replaced");
    map.insert("html_cache_99", "inserted");

    println!("cache is: {:#?}", map);
}
2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.