Temporary values' memory location

Hi there, I've been playing around with lsm303agr located on the microbit-v2 and faced up with strange behaviour (or I'm lack of knowledge how it works).
I was getting an error that write buffer is out of DMA range caused by the lsm303agr code

lsm303agr read_register
fn read_register<R: RegRead>(&mut self, address: u8) -> Result<R::Output, Error<E>> {
        let mut data = [0];
        self.i2c
            .write_read(address, &[R::ADDR], &mut data) // &[R::ADDR] is an error cause
            .map_err(Error::Comm)?;

        Ok(R::from_data(data[0]))
    }

Later I've found out creating a slice in place locates it out of DMA whereas creating an array and reference it works fine (thought slice in place creates an array implicitly). Overall creating temporary values places far away in memory compared to taking a reference to the existing ones (playground link to the snippet below)

fn main() {
    let arr = [21_u8];
    let p = &arr;
    let s = &[39_u8];
    println!(
        "Array ref [{:?}]\nSlice ref [{:?}]",
        p.as_ptr() as usize,
        s.as_ptr() as usize
    );
    
    let n = 21;
    let rn = &n;
    let r = &39;
    println!(
        "[{:?}]\n[{:?}]",
        rn as *const _ as usize,
        r as *const _ as usize
    );
}

I'm curious about the behaviour, how it works and why it's so
Would appreciate any help to reveal the internals
Thanks in advance :slightly_smiling_face:

1 Like

Some values will be immutable constants hardcoded into the executable. This is what makes &[1] even possible, because slices have to borrow from somewhere that is already stored in memory.

Other values may need to be created on the stack dynamically.

And finally, for small values like integers, their location is fiction created only because you’ve asked. They’re usually held in registers and don’t have a memory location at all. But if you force them to print some address, the compiler will be forced to move them somewhere where they have an address.

1 Like

Well, looks like &[T] is hardcoded into an executable, isn’t it?
As I know such expression expands into

let t = [T; n];
let v = &t;

Nonetheless does it make a compiler to place it into an executable whereas explicit array on the stack dynamically?

It does not expand to such expression. It's:

const T: [T; n];
let v = &T;

where const is magically materialized where it's needed, and theoretically doesn't even have its own stable address.

1 Like

Ah, that’s the piece I had misconception about
Now it’s completely clear
Many thanks :slightly_smiling_face:

That's too absolute. &[...] can be a temporary, or even an extended temporary:

fn foo(x: i32) {
    let y = &[x]; // x is not a constant!
    println!("{y:?}");
}

What is being demonstrated here is not that &[39] always points to a constant, but that the compiler is allowed to place the values of constant expressions in static memory and reference them from there.

1 Like

Well, this is a magical handling of let x = &… pattern, which is a purely syntactic hack in Rust for making a temporary variable and then taking its address. It's not making a slice, it's making an array on stack and then taking its address. If you do the same thing but with non-trivial syntax, it won't work.

fn this_does_nothing_and_yet<T>(t: T) -> T { t }

fn xx(x: i32) {
   let yep = &[x];
   drop(yep);
   let nope = this_does_nothing_and_yet(&[x]);
   drop(nope);
}

Perhaps I shouldn't have included temporary lifetime extension, then. My point is that nothing here is required to be a constant. There is no trivial desugaring that produces a const item always from &[...].

It does make a difference:

fn xx(x: i32) {
   let constant = this_does_nothing_and_yet(&[1]);
   drop(constant);
   let nope = this_does_nothing_and_yet(&[x]);
   drop(nope);
}

constant compiles, variable doesn't. let x = &T is a very different construct from &T, which IMHO is a terrible gotcha that makes understanding Rust's semantics harder.

On rereading your previous post, I now see that I misunderstood what you were saying. I read it as "&[...] will always introduce a const item", which is impossible, because the array might be made of non-constant values. I now understand that you might mean that an (effective) const item is introduced if needed.

Sorry I wasn't clear. I've had in mind the 'static literals, rather than the ones that get implicitly created as locals/temporaries.