Does mutably borrowing a const struct completely clone it?

I was quite surprised that this code compiles:

#[derive(Debug)]
struct X {
    a: i32,
    b: i32,
}

const SOME_CONST: [X; 2]=[
    X {
        a: 0,
        b: 0,
    },
    X {
        a: 0,
        b: 0,
    },
];

fn function(v: &mut [X; 2]){
    v[0].a = 10;
    println!("{:?}", v);
}

fn main() {
    function(&mut SOME_CONST);
}

How can this work? Is SOME_CONST inlined and a completely new struct created? I would have expected that I need to call clone() because this sounds expensive.

I stumbled upon this in http://cliffle.com/p/dangerust/5/ in the section "Removing the unsafe block from main". Changing a static mut to such a const + local variable makes the program faster, which I do not understand, either. I guess that now the struct is on the stack instead of some static RAM section, but why does this make it faster? In the section " Getting the static out" this made the program slower because http://cliffle.com/p/dangerust/5/#addressing.

Basically yes, constants are created anew each time they are mentioned. Mutations to that temporary value will not be seen in other instances of that same constant.

No, clone it not called. const works just like C #define with some extra type information. You main function will literally turn into

fn main() {
    function(&mut [
    X {
        a: 0,
        b: 0,
    },
    X {
        a: 0,
        b: 0,
    },
]);
}
1 Like

It literally involves (at most) moving around 4 integers. That's not expensive by any standards today, even on low-resource systems.

Of course it's not expensive in my example, but with bigger structs.

In the case of const values, the copy is done "at compile time" :slight_smile:

2 Likes

If your values are so big that a memcpy is a performance problem, you shouldn't be declaring them as const. Consider using static or lazy_static! instead.

For big constants, I'd recommend defining it as a reference, then the copied instances are just pointers to the same static memory.

2 Likes

Like this:
const SOME_CONST: &[X; 2]=&[...
?

But the complete struct has to have been loaded into RAM (because I could mutate it). How can this be done at compile time?

Yes, exactly.

Your code gets transformed into (something like) this at compile time:

#[derive(Debug)]
struct X {
    a: i32,
    b: i32,
}

fn function(v: &mut [X; 2]){
    v[0].a = 10;
    println!("{:?}", v);
}

fn main() {
    let mut x = [
        X {
            a: 0,
            b: 0,
        },
        X {
            a: 0,
            b: 0,
        },
    ];

    function(&mut x);
}

You're not mutating SOME_CONST, as that ceases to exist at runtime. You're mutating the compiler-generated x variable.

const is basically a way of saying, run this code at compile time, then copy and paste the output whereever the name is used.