both require a `fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>` function
while they work the same semantically it's "convert to string for displaying" and convert to string for debugging" (where debugging could be log messages, or print line debugging etc. by default debug for strings will do string escaping and for structs does print the structure/fields/type name in some semi standard way).
the Formatter is mostly just a string writer/sink, which also exposes a fixed set of formatting options and some utility methods
if a type implements Display through stuff I will skip over you then can also write `instance_of_type.to_string()` and get a string.
This trait are used in the default formatting mechanism used by e.g. `println!()` which other libraries (e.g. log) can use through `format!` and some other more internal/95% of case you don't need to know stuff. E.g. `println!("{a}, {a:?}, {a:#?}")` will in order print the display formatting of a, the debug formatting of a and then the alternative debug formatting (if there is one, else the normal one). And other options like pad left, floating point format options etc. exist too.
Through it should be noted that rust doesn't contain build in serialization, through nearly everything uses the library/traits/types from `serde` for that.
I am saying I much prefer reference implementations to be written in C, as it is pretty straightforward to understand, just like pseudocode would, but you can compile and run a C program.