Address of associated constant

Hi,
I’m looking for a way to represent some custom type information using an associated constant. Basically what I’m trying to do is the following (bear in mind I'm new to Rust):

struct Info {
   name: &'static str
}

trait Thing {
   const INFO: Info;
}

struct ThingA {
}

impl Thing for ThingA {
   const INFO: Info = Info { name: "Info" };
}

fn func<T: Thing>() {
   println!("From func: {:?}", &T::INFO as *const Info);
}

fn main() {
   println!("From main: {:?}", &ThingA::INFO as *const Info);
   func::<ThingA>();
}

When I run this code on my Mac the address printed by main and func varies in debug and release. However, this is not the case with Playground where debug and release print the same address. Both my Mac and Playground uses rust 1.52.0 (I’ve double checked this with cargo -V).

  1. Any idea why I’m seeing this behaviour?

  2. I want to be able to identify types and I’m trying to mimic a solution possible in C++, taking an address of a const static member. I could probably switch to TypeId but I’m curious as to whether something like what’s shown in the example above is possible/appropriate in Rust, and if not what the proper solution in Rust could look like.

Many thanks,
Michael

1 Like

I guess the simplest answer for why is just: They can be different, so they sometimes are. See the Rust Reference which says

References to the same constant are not necessarily guaranteed to refer to the same memory address.

I don’t exactly know why the playground behaves different from local tests here; maybe it’s some form of platform dependence, or something else, who knows… In any case you should apparently never rely on those addresses being the same. You probably shouldn’t try to rely on different consts having different addresses either.

Using TypeId to identify types seems like a very good idea.

1 Like

The INFO field is just stored as a constant inside the binary which then gets mapped to part of your program's address space. I'm guessing the address is different between runs because of Address Space Layout Randomisation which is a security feature that alters where the binary is mapped to make it harder to write exploits that hard-code addresses/function pointers.

This is fine. Your approach works exactly as you intended and the value being pointed to is exactly what you expect.

I think this is about the address being different between main and func, i.e. I’m getting outputs like

From main: 0x555bd3049548
From func: 0x555bd3049580

on debug.

This playground may give a better hint as to why DEBUG on the Playground still showed the same address: the playground compiles with codegen-units=1, even on debug.

  • EDIT: the previous playground results may not be obvious to read, especially on 1.52.1, so see this new one for a better explanation that works on 1.52.1
5 Likes

Ah, totally missed that, thanks for the reference. That settles it then, cannot use this solution to achieve what I'm after. Will go with TypeId instead.

Thanks

This is what I was expecting coming from C++, that the INFO field would be stored at a specific location in read only memory and I would therefore be able to use its address to uniquely identify types but as pointed out by steffahn this isn't the case.

I’ve never seen such an abuse of the rust playground system…

Anyways, I can improve this… see, I’ve fixed syntax highlighting for you :sweat_smile:.

6 Likes

First off, even in C++, you couldn't rely on a static having exactly the same address (i.e. numerically the same pointer) across different builds, optimization settings, or executions of the same program image.

Second, Rust also has static, so if you want something of which the address remains the same strictly only within a single execution of a given program image, you can use it just like you would use a C++ static.

Lastly, although you could do that, I would consider it to be abuse of the feature. Exactly because you could do it using Rust's const feature, which is nothing like static. Basically, const expressions are more like literals that get copy-and-pasted into the context where they are mentioned. However, their value is guaranteed to be consistent. So if you are trying to identify something uniquely, use the value of a const expression instead.

1 Like

“Their value is consistent” has the same caveat around references, too, since (citing the same reference page)

Constants may refer to the address of other constants, in which case the address will have elided lifetimes where applicable, otherwise – in most cases – defaulting to the static lifetime. (See static lifetime elision.) The compiler is, however, still at liberty to translate the constant many times, so the address referred to may not be stable.

Of course, but I meant something like using a const ID1: u32 = 42;.

Yes, you are exactly right right regards to C++. But just to be clear, in this specific case I did not aim to rely on this information being the same across any of the situations you describe, I'm only looking for some means to uniquely identify a specific type during a specific execution of my program.

In my case, using static, as least as far as I understand them, would be clunky since they cannot be declared inside the type itself. I imagine I would have to use an associated function on my types that returns the address of the static Info instance that has to exist at the module level to achieve this.

Also, the reason I'm looking for something as lean as an address to uniquely identify the types is because I expect Info in this case to grow.

If that is the case, why can't you use Info by value? From what I can see, using a pointer to a static member and checking its address is just another way of saying "are these two Info instances equivalent".

You can also derive PartialEq and Eq for checking that two Info instances are equal (i.e. ==) and Hash so you can use it as the key for a hash map.

1 Like