This is helpful but in general what is a problem of having strings and trivial functions to convert something (like bytes) to strings? Maybe I am missing the point.
Because converting a type to a string isn't trivial. It needs to be defined for that type.
Rust does have a pair of traits that can be used, Display [1] and Debug [2]. Display must be implemented manually by the author of a type, while Debug can be automatically derived if all the members of a type implement Debug. Like this:
The process of "functions to convert bytes to a string" is called "encoding". The problem is, there's no universal way to do it, so you need to choose one. You need to know what the bytes mean, and then convert them into whatever representation that you need.
It is unfortunate that you’ve been downvoted for asking a question.
Happy to help. Feel free to post questions on users.rust-lang.org as well; people are super happy to elaborate on any question, no matter how big or small.
Rust doesn't have varargs, so formatting functions tend to be macros (allowing to pass in multiple arguments, compile time parsing the format string to allow type checking etc) so they are generally not that trivial.
There are some examples of printing output using formatting strings in the RustCrypto readme:
let hash = Blake2b::digest(b"my message");
println!("Result: {:x}", hash);
If you're looking to get the actual string value rather than just print it out, then take a look at the format macro. It uses the same format strings / traits etc as println, so wherever you see println you could drop in format instead:
let hash = Blake2b::digest(b"my message");
let text = format!("{:x}", hash);
It sounds like you're coming from a language (like C) where a string is just an array of bytes or a language (like Ruby or Javascript) where strings are seriously overloaded.
In Rust a string is a UTF-8 sequence. Typically human readable. A byte is generally represented by the u8 type, and a collection of bytes is either an array or vector (e.g. [u8] or Vec<u8>). To create a human readable form of an object in Rust you'd typically use the Display ({} format specifier) and/or Debug traits ({:?}). A string (e.g. &str or String) is NOT a collection of bytes. There are exceptions however with OsString/OsStr representing something closer to a collection of bytes and CString/CStr representing a bag of bytes.
Your comments seem a bit XY-ish to me. What are you trying to solve by converting things to strings? For debugging or human readable output Array, Vec, and u8 all implement the Debug trait. u8 also implements UpperHex and LowerHex so you can get a hex formatted version as well (e.g. format!("{:02X}", bytes)).
For the (MD5) hash case, as others have pointed out you're looking at a base 64 encoding which you'll often have to do on your own depending on the library you're using.
Now if you're struggling with owned vs borrowed strings that's a whole other matter. You can insert some magic into your functions with generics and trait constraints and into your structures with the Cow type (clone on write).