1
\$\begingroup\$

Let's consider you want to clone all items of a vector orig except the item of index i should be taken from alternative value alt.

Lets say the items are of the type

struct Bla{
  // Some fields
}

Using for loop

On simple solution would be:

fn clone(orig: &Vec<Bla>, i: usize, alt: Bla) -> Vec<Bla> {
  let cloned = Vec::new();
  for (j, item) in orig.iter().enumerate() {
    let new_item = if i != j {item.clone()} else {alt};
    cloned.push(new_item);
  }  

  cloned
}

Note: Does not compile, because the compiler does not know that i==j is only valid once.

Using functional style

The functional alternative would be the follow:

fn clone(orig: &Vec<Bla>, i: usize, alt: Bla) -> Vec<Bla> {
  orig.iter().enumerate()
    .map(|(j, item)| if i != j {item.clone()} else {alt})
    .collect::<Vec<Bla>>()
}

The last one looks nice, but does not compile :( because the rust compiler does not know that alt gets moved (or consumed) only once (i==j).

Using mem replace

In order to fix the version above one can use a default value for the index which should be replaced.

fn clone(orig: &Vec<Bla>, i: usize, alt: Bla) -> Vec<Bla> {
  let mut result = orig.iter().enumerate()
    .map(|(j, item)| if i != j {item.clone()} else {Bla::default()})
    .collect::<Vec<Bla>>();

  mem::replace(&mut result[i], alt);
  result
}

What is the most concise and fastest implementation to do so?

\$\endgroup\$

closed as off-topic by Toby Speight, Grajdeanu Alex., Graipher, pacmaninbw, Austin Hastings Apr 16 at 22:07

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Lacks concrete context: Code Review requires concrete code from a project, with sufficient context for reviewers to understand how that code is used. Pseudocode, stub code, hypothetical code, obfuscated code, and generic best practices are outside the scope of this site." – Toby Speight, Grajdeanu Alex., Graipher, pacmaninbw, Austin Hastings
If this question can be reworded to fit the rules in the help center, please edit the question.

0
\$\begingroup\$

In my opinion none of your solution is idiomatic and all use an useless comparaison.

Using for loop:

  • Doesn't pre-allocate the vector
  • Use a comparaison that could be avoid

Using functional style:

  • Use a comparaison that could be avoid

Using mem replace:

  • Use a comparaison that could be avoid
  • Is unnecessary complex to just copy a vector

Also, you should avoid take a &Vec<T> as parameter, it's much better to take a &[T]. See: Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?

I would use one of the following version:

#[derive(Clone)]
struct Pokemon {
}

fn a(pokemons: &[Pokemon], i: usize, pokemon: Pokemon) -> Vec<Pokemon> {
    let mut digimons = Vec::with_capacity(pokemons.len());

    digimons.extend_from_slice(&pokemons[0..i]);
    digimons.push(pokemon);
    digimons.extend_from_slice(&pokemons[i + 1..]);

    digimons
}

fn b(pokemons: &[Pokemon], i: usize, pokemon: Pokemon) -> Vec<Pokemon> {
    let mut digimons = pokemons.to_vec();
    digimons[i] = pokemon;

    digimons
}

fn c(pokemons: &[Pokemon], i: usize, pokemon: Pokemon) -> Vec<Pokemon> {
    pokemons[0..i]
        .iter()
        .cloned()
        .chain(std::iter::once(pokemon))
        .chain(pokemons[i + 1..].iter().cloned())
        .collect()
}

I don't really know witch version is better, a() and c() should be the same but sometime chain() is not optimized away. And b() is probably the most straightforward solution. I think I would choice a() in a production code.

However, looking at the assembly, we can see that a() and c() doesn't produce the same, and that b() is the simplest.

\$\endgroup\$
  • \$\begingroup\$ But solution a() and c() avoid copying/cloning ith element unnecessary from the incomming slice. \$\endgroup\$ – Matthias Apr 12 at 14:51
  • \$\begingroup\$ @Matthias depend on the cost of clone, b() "could" be faster. If you really want to be sure, you must do some bench-marking. \$\endgroup\$ – Stargateur Apr 12 at 16:19

Not the answer you're looking for? Browse other questions tagged or ask your own question.