I think it really depends on what you're used to.
You could format this alternately as:
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
using the `where` clause makes it easier to see the bits, in my opinion. That said, even with your version, you can read it almost front to back: "A function named deserialize that takes one type parameter, D, that implements the Deserializer trait. It takes one argument of type D, and returns a Result of either Self or D's error type."
The symbols in this example:
* <> are used for generics, which is common in other languages.
* : is for two things: trait bounds; that is, restricting a type parameter by a given trait. I'm not sure where this came from, but it feels fairly intuitive after a bit, at least to me. Second, for indicating the type of arguments. Both are about defining the types of things, so even though they're different, they feel consistent, at least to me.
* ' is used for lifetimes; this comes from OCaml, which many programmers aren't familiar with. Nobody likes this syntax, but nobody could come up with a better syntax.
* () is used for the parameter list to a function, this is extremely common in languages.
* -> indicates the return type
* :: is for navigating namespaces; the first instance is for a module, the latter is for an associated type. Similar to :, these are different but similar things, so the similar syntax makes it easier to me.
It's all just practice. Being familiar with a wide variety of languages helps, because Rust draws from a lot of other places.