And I totally agree with the conclusion of the article: don't implement Deref yourself.
Mistakes with Rust smart pointers: when Deref goes wrong
41–50 of 64 posts
Re: Mistakes with Rust smart pointers: when Deref goes wrong
#42I think I've hit the same kind of problem once. What I learned is that true smart pointers are types that (at least in Rust) aren't really supposed to have methods on their own, to avoid this type of ambiguity during method resolution. For example, Box implements Deref so that you can conveniently use T's methods. If you look at the documentation of Box, the things you can do with the Box itself , like Box::leak, are…
IMHO there the logic behind that is very clear and makes a lot of sense. I think it's natural that view-related traits (the ones who operate on the elements of a contiguous area of memory) should be associated with slices, while memory-related ones (the ones that operate on an owning container) should be implemented on the owning types. If you mix them up, you are bound to mess up first and foremost because _it is not logically sound_. You don't need to own a string in order to check if all of its ASCII characters are all caps, for instance, because it's an operation that's also valid on any array of bytes, without the whole concept of "owning memory".
Re: Mistakes with Rust smart pointers: when Deref goes wrong
#43What happened is that in many usages the compiler would insert an implicit deref which would lead to an `Index` implementation being available.
But this does not work in all cases. Not just the case mentioned in the article but many others. The simplest is that it doesn't implement the `Index` trait and hence any generic bound requiring `Index` will fail if you try to use it with a `AsciiString`.
So it important to _not think of `Deref` as a form of inheritance ever_ nor use this as a oversimplifications when teaching rust or similar, it will only lead to a lot of confusion.
Additionally smart pointer in rust are "pointer like" which means you don't expect them to have methods by themself or anything like that. Hence why special methods on smart pointers are implemented as static function on the type, instead of methods.
Through `Deref` can also idiomatically be implemented for types with a owned/borrowed duality (e.g. `String`/`&str`) so `AsciiString/AsciiStr` would work but `AsciiString/str` won't be ideomatic.
Then if you idiomatically implement traits on this types you won't run into the issue described in the post, as you implement all you `Index` types and similar on the `&AsciiStr`/`&mut AsciiStr` etc.
Anyway there is a way to get a reference to a different kind of type, e.g. getting a `&str` from a `AsciiString`. It's `AsRef`. But that is not automatically applied like `Deref` for the reasons many reasons including stuff like mentioned in the Article.
Lastly a better way to think about why it doesn't work is that the type resolver looks if the type is implemented on your concrete type in some form and only if it isn't it will potentially dereference. And due to technical details of how type resolving reliable in multiple direction can be tricky `Index` does count here as "the type is implemented" even if the `T` doesn't match.
The articles conclusion of not implementing `Deref` (in most cases) is something I can fully agree with.
Re: Mistakes with Rust smart pointers: when Deref goes wrong
#44Re: Mistakes with Rust smart pointers: when Deref goes wrong
#45Deref polymorphism is a known anti pattern. https://rust-unofficial.github.io/patterns/anti_patterns/der...
Re: Mistakes with Rust smart pointers: when Deref goes wrong
#46"The issue, it turns out, is that method search does not check method parameter types against argument types." Right. Rust's doesn't allow much overloading of function names. Partly because C++ did. The C++ overload resolution rules got very complex, especially since they interact with implicit conversions. To keep those rules from introducing errors, there's a rule in C++ that the overload chosen must be at least on…
Re: Mistakes with Rust smart pointers: when Deref goes wrong
#47I think I've hit the same kind of problem once. What I learned is that true smart pointers are types that (at least in Rust) aren't really supposed to have methods on their own, to avoid this type of ambiguity during method resolution. For example, Box implements Deref so that you can conveniently use T's methods. If you look at the documentation of Box, the things you can do with the Box itself , like Box::leak, are…
Deref for Vec and String is fine because they are owning containers that decay to their "view" types (slices). Strings and vectors own memory, while views borrow it and can be used to operate on them without reallocating or moving the container itself, treating it as a contiguous list of values. A similar relationship exists between C++'s `std::string` and `std::string_view`, and `std::vector` and `std::span`. IMHO t…
Re: Mistakes with Rust smart pointers: when Deref goes wrong
#48From the start, I wished that Rust had used different punctuation for method calls on a `T` and a `SmartPtr`. For instance, `my_t.f()` but `my_boxed_t->f()` (or get rid of auto Deref and just require `(my_boxed_t).f()`). For subscripting it could be something like `my_vec->[index]` (or just require `(my_vec)[index]`. Just something there to remind you that you are in fact using a smart pointer. This would also let you distinguish between `box.leak()` and `box->leak()` and not require `Box::leak(box)`.
Re: Mistakes with Rust smart pointers: when Deref goes wrong
#49I think I've hit the same kind of problem once. What I learned is that true smart pointers are types that (at least in Rust) aren't really supposed to have methods on their own, to avoid this type of ambiguity during method resolution. For example, Box implements Deref so that you can conveniently use T's methods. If you look at the documentation of Box, the things you can do with the Box itself , like Box::leak, are…
Deref for Vec and String is fine because they are owning containers that decay to their "view" types (slices). Strings and vectors own memory, while views borrow it and can be used to operate on them without reallocating or moving the container itself, treating it as a contiguous list of values. A similar relationship exists between C++'s `std::string` and `std::string_view`, and `std::vector` and `std::span`. IMHO t…
Interestingly you can ask whether a string is ASCII, "this".is_ascii() is a predicate which does exactly that, but for what I think is your question, "are they all ASCII caps?" you'd need to go via an iterator and a lambda: "THIS".bytes().all(|b| b.is_ascii_uppercase())
The iterator is because there is no provision of each such predicate over the whole string, which seems reasonable (the character class predicates are provided on u8 (ASCII only), and on char (all of them)), but the lambda is due to Rust's 1.0 compatibility commitment plus an unfortunate historical choice. If not for compatibility we'd fix it so you could just write "THIS".bytes().all(u8::is_ascii_uppercase)
Re: Mistakes with Rust smart pointers: when Deref goes wrong
#50I think I've hit the same kind of problem once. What I learned is that true smart pointers are types that (at least in Rust) aren't really supposed to have methods on their own, to avoid this type of ambiguity during method resolution. For example, Box implements Deref so that you can conveniently use T's methods. If you look at the documentation of Box, the things you can do with the Box itself , like Box::leak, are…
I imagine this is explicitly state in some portion that I have never read of the Deref documentation. The thing is, every introductory text ignores this, and there is no itemized rule in a more global document so that things like Rc and Cell can point directly to it.
The Rust docs have a very general discoverability problem, and this is one of its facets. Since things have a very generic and reusable definition, the docs have no good place to put information like that.