4 ms·
Wait what? You can use dyn on the stack (without a Box)? This has been one of my biggest complaints about Rust: I've been using it for years at this point. I r
by brundolf 6y ago
Wait what? You can use dyn on the stack (without a Box)?
This has been one of my biggest complaints about Rust: I've been using it for years at this point. I read most of the Book, I've read a few unofficial books. And I do love the language, but it has so many cases like this where things that you're allowed to do (syntactically or otherwise) are somehow so non-obvious that you can miss them entirely. I still get blindsided by something like this every couple months, and I always end up a little mad that I've been doing things the hard way until some obscure unofficial material (or more often, a stack overflow answer) teaches me about an entire feature that I didn't know existed.
Rust is really great at telling you what you can't do, and in many ways its documentation is incredibly thorough, but it has a real problem when it comes to discoverability and establishing a consistent mental-model of what its syntax actually means (and how you can then apply it to other situations). I don't know what the root cause of this problem is. But it's really distressing to me each time I discover a huge blind-spot; it makes me feel like I never fully understood the language concepts that I thought I understood.
I say all of this out of love: I really want Rust to succeed. I still prefer to use it despite this issue. I just believe this is a huge thorn in its side, especially when it comes to adoption, for which it already has an uphill climb.
- steveklabnik 6y agodyn just requires the object to be behind some kind of pointer. The vast majority of the time, that pointer is a Box or an Rc/Arc, but any form of indirection can work.
- brundolf 6y agoTaking a second look at this particular example, I guess it is a bit of a "trick" because of the ahead-declaration of both possible "holder" variables on the stack. It's just... wildly unintuitive that this should be possible. Even if you showed me this code without saying whether or not it should compile, I wouldn't be sure. Here's the train of intuition: 1) dyn requires a pointer that may be to one of multiple types of structs 2) a group of multiple types of structs has an undefined memory layout, so the value must either live on the heap or be wrapped up in an enum That feels like an airtight understanding. But then Rust lets you do this weird juggling maneuver based on control-flow that allows you to do it on the stack. I'm not saying Rust shouldn't let you do this, and I'm not really sure how it could be made intuitive given the "normal" case. I'm just expressing that subjectively, this feels very weird and non-obvious, and it's far from the first example like this that I've encountered. Here's another example: https://news.ycombinator.com/item?id=25595120 https://news.ycombinator.com/item?id=25595120
- steveklabnik 6y agoI think where your intuition is leading you wrong is that in #2, you are assuming ownership. That is, you're saying "I need a place to put an arbitrary thing." But you don't! A &dyn T doesn't own T, just like a &T doesn't own T. Trait objects are a (pointer to data, pointer to vtable), (may be in the reverse order we don't guarantee layout) and so that pointer can point to anywhere, heap or stack. Interestingly enough, this was special in Rust 1.0 to Rust 1.5. In 1.5, it finally became non-special. It's interesting because I totally get what you're saying, but at the same time, this is an example of Rust being orthogonal, not special cased. (It is really hard to address the string thing without an example, to be honest.)
- brundolf 6y agoI think you're right about my intuition, and that's interesting about the syntax change. It could just be that during my learning stage I learned (based on example bias maybe?) that dyn is for owned values, and not just any reference. I'm sure this was never stated explicitly, but somehow that idea got lodged in my brain For the String thing (really, the Deref thing), further down others weigh in with a case where it doesn't work as expected, and then the workaround &*. The latter is something that feels like it should be a non-op, yet it's required in certain cases like this one to trigger something in the compiler. I'm sure there's some internal reason for this, but from the user's perspective it's, "What does dereferencing and then re-referencing this value have to do with performing what amounts to a cast?" I want to emphasize that I'm not complaining just to complain, nor placing blame on any specific party. I'm just "reporting a bug" in my learning experience with the language, and trying to provide as much info as possible :)
- steveklabnik 6y ago> I want to emphasize that I'm not complaining just to complain, Oh yeah totally! It is very helpful. > It could just be that during my learning stage I learned I mean, I think this is very reasonable and intentional. Trait objects are a pretty niche feature of Rust already, and non-owned trait objects are even more niche than that. The book does guide you towards Box<dyn Trait> for this reason. > or the String thing (really, the Deref thing), further down others weigh in with a case where it doesn't work as expected, Yeah so the trick here is a balance between not wanting coercion willy-nilly, and also making some cases work well. You had cited method calls specifically, and those should work due to auto-ref/deref. The example given isn't about method calls, it's about match not doing Deref coercion. That being said it's really easy to assume that it always does it, because it does do it in the right places most of the time! There's interesting tradeoffs here...
- staticassertion 6y agoHonestly, who cares about not knowing that something was possible if you never wanted it? `dyn` working on the stack is more consistent than it not working on the stack. I'd say one way to a language more completely is to: a) Read its stackoverflow pages (not sure about Rust but this helped a ton with C++) b) Get code reviews from others. The rust community is fairly active, or at least it was back when there was just an IRC channel, I don't know as much now since I don't get onto whatever the medium is today. Ask people if there's an easier way to do something. c) Read others code. I, for example, like to review some of my dependencies just so I understand a bit more about how they work, and I've picked up a lot from that. I used to get on IRC in the morning while I was kinda getting into my work-day and see if I couldn't help others out, this really taught me a lot.
- brundolf 6y ago> who cares about not knowing that something was possible if you never wanted it? I have wanted that feature at times in the past (or something like it), and wasn't able to determine that it would be possible. That's exactly my point. As for your suggestions: this is indeed one way to gather this knowledge, and I have learned a lot particularly from looking at stack overflow. But I don't think it's ideal when knowledge about fundamental language features has to be acquired by word-of-mouth, because it wasn't conveyed in the course of the normal learning path (official tutorials + discovery through application of learned concepts to new situations where their relevance is self-evident).
- staticassertion 6y agoI guess when I assume a feature is possible, and I want to use that feature, I try it and see if it works. I also don't typically learn via books myself, I learned rust pre-book, and basically entirely via IRC. To me, that's the default-path. But I get why it's not ideal - a written record (like the one linked) is definitely important to have, and it is a legitimate weakness that they're so nascent.