Implement `Display` for raw pointers

To format the value of a pointer, you can currently only use {:p} or {:?}, as a *const T or *mut T only implement Pointer and Debug.

Although both formatting types are currently equal for pointers, using {:?} doesn't always feel the right choice because of semantic reasons (it's called "debug" after all) and the fact that its output is unstable according to the standard; it is subject to change from version to version.

However, {:p} has a problem. Specifically, references also implement the Pointer trait. This means that if you use {:p} to display your *const T, and you're not careful enough to dereference any layers of references you have to it, it will display the address of the (ref to) pointer, instead of its value.

Behold: Rust Playground

There is currently no way to explicitly format something as a pointer, like {:p}, but with all the implicit dereferences you get with any other type of formatter. I would therefore suggest to add blanket implementations of Display for *const T and *mut T, that guarantee the same output as {:p}, but with collapsing references, so you can just use {} like most other values.

4 Likes

Display is for user-facing output. Pointer addresses are definitely not. I feel that Debug is good enough here, given that people are unlikely to really need to print the pointer value other than for debugging reasons (and if they really want, they can cast it to usize and print that), and that we're unlikely to change this implementation unless we find something vastly better.

6 Likes

I don't really have an opinion on this, but...

This assumes a specific user-base. If someone is trying to print a pointer to the user, then I wouldn't feel comfortable telling them "that's wrong and you shouldn't print pointers to users." That's a choice for the application/library developer, not the language.

4 Likes

I agree with @mjbshaw, "users" include technical people that need to be able read the address of the pointer. For example, when creating a hex editor or disassembler with Rust. While std::fmt::Debug is very much for debugging the application itself. Clearly the programmer has made the choice to output the value of a pointer. I don't see why it shouldn't be allowed to have a default formatter.

As for:

and that we're unlikely to change this implementation unless we find something vastly better.

For clarity, I was suggesting adding an implementation, not changing an existing implementation that would break current code.

I think it’s telling that the examples you’ve come up with aren’t examples where you have in-memory pointers. There really aren’t reasons to print pointers within a process’s own address space that aren’t about debugging, and *const T is the wrong type to use for pointers outside the process’s address space. Even for something like a JIT-interpreter or an in-process debugger, it would make sense to have a separate type for “pointers to memory being scrutinized” and “pointers that might very well be in my own memory”.

That said, “Display is for showing to users”, but it isn’t localized.

4 Likes

I would find it somewhat confusing if the Display and Pointer impls for something both printed pointers, but printed different values.

I don't think this is inconsistent either, since it agrees with other pointer-like types. For example, Box implements fmt::Pointer but you can print the box (pointer to heap) and a reference to the box (pointer to that pointer to the heap) separately:

fn main() {
    let b = Box::new(100u32);
    println!("{b:p}");

    let b_ref = &b;
    println!("{b_ref:p}");
}
0x55630541b9d0
0x7ffea18037f0

And stacked references don't deref, Rust Playground. You wouldn't expect a *const *const T to deref the outer pointer and display the inner value either.

That being said, I agree that this can be confusing. Perhaps there is a solution to make this more clear without going through Display.

1 Like

You can use a function like this to get the pointer out of any number of reference indirections:

fn ptr<T>(p: & *const T) -> *const T {
    *p
}

Maybe something like it could live in the standard library?

Again, you're assuming specific apps or users.

If I'm writing a debugger then yes, I have real pointers that I need to print the address of to the user.

Or if I'm writing a forensics tool to read memory, I have real pointers that I need to print to the user.

It's fine to argue that these use cases are uncommon, but it's bad to argue that they don't exist.

Again, those pointers are not in the debugger’s/tool’s address space, and representing them with *const T would be incorrect. (Consider debugging a 64-bit process from a 32-bit debugger, as an extreme example.)

3 Likes

Again, you keep assuming things.

In-process debugging is absolutely a thing, particularly for interpreted embedded languages. For example, AngelScript.

And not every host uses virtual addressing. Scanning memory on a microcontroller that uses global addressing is indeed a real thing people do. In that case, using real pointers is not "incorrect."

I’m sorry; you’re absolutely correct. I still don’t think those reasons are sufficient for pointers being Display, but I was being overly circumscriptive (and underly imaginative).

In an ideal world, yes. But often you deal with native pre-defined structs that include those pointers. It's very convenient to use those structs in your application and copy relevant data into them, rather than redefining every struct that uses a pointer because of some pedantic reason that a pointer should only point to something within your address space.

But don't you find it similarly confusing that this is currently already happening between the Debug and Pointer impls?

Yeah, it definitely is. I feel like the Debug implementation is probably there mostly so you can #[Derive] on it. In general it just seems weird to be able to format a raw pointer with anything other than :p.

I can see an argument to be made here either way. Maybe you just want to post an ACP to get team feedback?

1 Like

I'm a bit unfamiliar with the processes involved. I was originally about to open an issue on github but the new issue template directed me here for feature requests.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.