Auto-impl `Debug` for all types when needed
Nobody has claimed this yet.
- Dominant language
- Markdown
- Stars
- 6.6k
- Forks
- 1.7k
- Avg merge
- 16h 14m
- Merged PRs (30d)
- 1
Description
Currently, two traits control the print fmt - Display and Debug. Display makes sense to be manually implemented. However Debug trait not only just quickly becomes tedious, but soon ends up in trait bounds and then starts to really pollute the code, even when it's unrelated to the problem space.
TL;DR:
format! ("{:?}", x)implies#derive[Debug] for typeof(x).- compiler relaxes the need for Debug as a trait, (in other words, it makes it invisible - and will simply derive it automatically if finds a call that implies
(x as Debug).fmt)
Eg:
Let's say one writes a naive algorithm for quicksort. And as a good programming citizen, leaves his/her debug/trace code, that may or may not get removed in the release build.
fn sort<T:Ord + Copy + Debug>(arr: &mut [T]) {
let len = arr.len();
if len < 1 {
return;
}
let mut wall = 0;
let mut index = 0;
let pivot = arr[len - 1];
debug!("pivot: {:?}, len: {:?}", pivot, len);
// ..snippety snip.. //
debug!("swap {:?} [index:{:?}] and {:?} [index:{:?}]",
arr[wall], wall, arr[index], index);
// ..snip.. //
}
debug!("wall: {:?}", wall);
// ..snippety snip.. //
}
Well, the problem here is I need the Debug trait bound, even though, it isn't a part of the actual problem space. And now every type passed to this generic method has to implement it in order for it to work. This is problematic. I think Debug has the potential to pollute the ecosystem this way.
Solution:
Learning from other languages (Go, .NET etc) - these compilers just do it for you automatically. Go runtime does the job of printing out, and .NET, for example automatically derives .ToString(). However, this is not ideal for the a zero-abstraction language like Rust, especially since some of them also use Reflection.
So, what I'd like to explore, is the possibility of letting the compiler auto-implement Debug in addition to #[derive(Debug)] for whichever types that leaves hints that it requires it. Hints could be in the form of compiler support that can be activated by the format! macros, and or more sophisticated analysis. So, there's always a default implementation, that provides a sensible format like Go. While a language like Go uses reflection, in Rust the compiler should have sufficient knowledge to print all of the data anyway, with the exception of references and pointers (which can just simply print the pointer values, since this can be overridden and by implementing the Debug trait manually.)
Hypothetically, if we assume this is exists, then in the above example Debug will no longer be a trait bound, since we can safely let the compiler satisfy it when needed. And since the compiler already has knowledge of what T is going to be, it will simply mark the type T to do #derive[Debug] when the compiler has direct access to T, or do a side implementation for it in the library being compiled, as it sees the format! macros, or whichever the mechanism to drop the hints are. Just to clarify - the format! macro is just a naive example to express the idea - it may or may not be the right place to drops the hints. But since Debug trait only really makes sense inside a format, I think that would be one way to do it. Or may be simply finding out all calls that imply (x as Debug).fmt would make more sense.
This effectively allows Debug to mostly become a forgotten hero behind the scenes. And println!("{:?}", x) should just work automatically.
However, I don't have knowledge of the compiler internals at the moment, so I'm unsure of the implementation details. I hope someone can shine some light on it.
EDIT: A little note on why analysis might be required as some have mentioned why not just let the compiler auto impl whenever needed - This will work fine for rlib and binaries (in fact, could be even the preferred way to do it), however it won't for dynamic libraries.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by examining the proposal's Debug, format!, and println! examples, including the stated distinction between rlibs, binaries, and dynamic libraries. Determine whether compiler-supported automatic implementations can satisfy the requested behavior without explicit trait bounds; done would require a defined design and implementation path, but the issue names no files or tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100