Why Copy is need in 'array repeat expression'

I'm new to Rust. Please correct me if i'm wrong.

struct S{
    i :u32,
}
impl Clone for S{
    fn clone(&self) -> S{
        return S{
            i: self.i,
        }
    }
}
// impl Copy for S{}

let arr = [ S{i:1}; 10];

compile error message: the trait Copy is not implemented for S
My rustc version is 1.49.0

I know I need to impl Copy for S. But why Copy trait is need here?

in my opinion, let arr = [s; 10]; can translate someting like this

for(i=0; i<10; i++){
    arr[i] = s.clone();
}

So I think impl Clone trait is enough.


If Copy trait is required, only copyable element can use this syntax.

let arr = [ "hello rust".to_string(); 10];  // error: String need impl Copy trait

Any help is appreciated!

In expressions that create arrays it's [array repeat expression; how many elements are in the array]. The expression is used to initialize every element of the array.

The expression must be copyable, because one expression is going to be copied to all elements of the array.

However, String in Rust is uniquely owned. It can't be trivially copied. It can be only manually cloned, and the array literal syntax doesn't do cloning.

This is a usability problem with fixed-size arrays in Rust. They're generally hard to work with. A simple workaround may be to use Vec instead, which clones its initialization expression:

let vec = vec![String::from("hello"); 10];

For fixed-size arrays there is a silly workaround involving two quirks:

  1. Empty String is a special case that can exist as a compile-time constant (other heap-allocated text can't, because there's no heap at compile time).
  2. Compile-time constants can be used to initialize arrays.
    const EMPTY_STRING: String = String::new();
    let arr = [EMPTY_STRING; 10];
1 Like

Thank you for your reply. I still dont understand this.

If I want to retain move semantics, how can I use this syntax?

struct Cloneable{
    i :u32,
}
impl Clone for Cloneable{
    fn clone(&self) -> Cloneable{
        return Cloneable{
            i: self.i,
        }
    }
}
impl Copy for Cloneable{}

let arr = [ Cloneable{i:1}; 10]; // Cloneable is forced to be Copyable

let s = Cloneable{i:0};
let r = s; //
let r2 = s; // move semantics is lost. So `s` is still valide.

Why not just require Cloneable (not Copyable), so that this syntax is more useable? Like

let a = [ "hello rust".to_string(); 10]; // String impl Clone. OK

struct S{
    // contains data on heap.  simple memcpy is not satisfied
}
impl Clone for S{
    fn clone(&self)->S{
        // data on heap is cloned properly
    }
}
let a = [ S{..}; 10]; // S impl Clone. OK

let s = S{..};
let r = s; 
let r2 = s;  // compile error! S retains move semantics. 

Some bookkeeping is required to know how many elements to drop if a panic happens while you're cloning elements. Vec keeps track of how many elements have been initialized but plain arrays do not. You can use the 'array-init' crate to efficiently initialize arrays without a Copy requirement and without leaking memory if initialization fails: https://crates.io/crates/array-init

You might argue that this functionality should be standard in Rust, but, for better or worse, it is not.

We don't have any syntax which calls .clone() implicitly. The vec![] is a macro which can be expanded to some code explicitly clone the element.

Thanks!
I guess what I need is something like vec![] but for plain arrays that can be used in const.

Then why Clone trait is need to impl Copy trait?

impl const Trait or something analogous will be needed for .clone() in a const context. A tool could perhaps be built around something else though, in the meanwhile. (Even then, Rust tends to prefer potentially expensive operations be more explicit; a macro or similar is more likely then implicit cloning IMO.)

It's easy to see how Copy is a specialized version of Clone, for one. Also, coherence says that there must be an unambiguous implementation of a trait for a given input type. So you can't have this, for example:

pub trait MyTrait {}
impl <T: Clone> MyTrait for T {}
impl <T: Copy> MyTrait for T {}

And just having Copy is often too limiting, as would be excluding types that are Copy. By making Clone a super trait, the restriction is lifted with an acceptable loss of generality (no Copy but non-Clone types).

And soon™, this will be able to be written as

    let arr = [const { String::new() }; 10];

as an explicit opt-in for "I want to use the const multiple times, not to Copy".

4 Likes

impl Copy basically means "I don't want move semantics".

From Rust's perspective this is an unsolvable problem. You try to implicitly init 10 elements with 1 object, but at the same time you want to forbid implicitly copying of the object.

Fixed-size arrays are awfully inflexible like that. They will not do what you want. Don't use them.

Use Vec. Use ArrayVec instead. These types support initializing with non-Copy types.

3 Likes

Thanks for your advice.
I give up with Fixed-size arrays and use Vec instead. :frowning:
Hope there will be a better version of Array, as Array is such a basic thing in programming.

Honestly, majority of the languages doesn't have fixed size array. Java, C#, Python, Ruby, swift, kotlin and JS for example.

2 Likes

The Rust equivalent to most other language's array is a Vec<T>.

Usually, for languages where the equivalent is not Vec<T>, such as Java where there is a fixed length int[] type, the actual equivalent is an Box<[T]>. You can create one of those by cloning an element with vec![elem; len].into_boxed_slice().

system programming language like c/c++

I'm not very sure but seems Box<T> is used with heap data.
https://doc.rust-lang.org/std/boxed/struct.Box.html

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.