Oh yeah, plenty of times.
I think I said somewhere before that making sense out of the ref keyword is actually the only part of the Rust language I ever struggled with. Everything else in the language was straightforward to me (at least in its basic use cases; things like the module system only got weird for me only when I dove into the details).
But here I have to completely disagree.
The reason ref was hard for me has nothing to do with the choice of keyword. For me, Rust is the first language that both has proper support for sum types including pattern matching, and distinguishes between value and pointer/reference types. That immediately implies that a pattern match must contain not only a pattern to decide if any given value matches successfully or not, but also a variable binding to be created if the match is successful. Before Rust, the closest thing I had to pattern matching was destructuring assignment in Javascript, but of course Javascript doesn’t have static types at all, much less separate value and reference types, so the binding part was just a name.
ref was hard for me because I completely missed the fact that a match arm has to contain both a pattern match and variable binding (even if the latter is always partially implicit and often completely implicit). Somehow, we must distinguish between matching on a reference (i.e., what let &x = y; does) and matching on a value to create a binding to a reference (i.e., what let ref x = y; does). Once I finally understood this, ref became easy for me.
It is easy to imagine lots of alternative syntaxes, but none that would really move the needle here. The hard part was internalizing why we need two separate syntaxes here in the first place, and none of the suggestions I’ve seen come close to helping with that.
This actually feels much worse to me, because this explanation implies the binding and the match are entangled and cannot be reasoned about separately. Today’s ref simply means “the binding will be a reference” but has no effect on the match. When you want to figure out what values a pattern will or won’t match against, you simply ignore all the refs. That’s nice and orthogonal.
I can see some parallels between this explanation and the details of how Javascript destructuring work (e.g., that let [a] = [10]; assigns 10 to a, rather than making a an array), but I think that design for destructuring syntax makes sense precisely because Javascript has no value vs ref types to worry about (not even with Typescript added on) so it has significantly less complexity to fit into that crowded space.
And finally, as scott hinted at, part of the motivation for the “match ergonomics” RFC was that it should make ref so exceedingly rare in practice that novices typically won’t need to learn it. I think that’s a far more effective solution to the problem than tweaking the syntax.