4 comments

  • saghm41 days ago
    &gt; I sometimes feel C and C++ were very clear on where data lives (stack vs heap) and how it’s organized (struct alignment), while Rust seems a little more opaque . I’ve felt a similar way working in Go.<p>This is interesting to me. I fully agree about this with Go (and in the past I&#x27;ve sometimes seen this make optimizations difficult as in practice it&#x27;s hard to keep track of heap allocations other than runtime inspection), but I feel like Rust is actually better at C++ than this. Alignment is certainly a different beast, as by default I don&#x27;t think you can really assume anything about how Rust will lay out a struct (with the workarounds being various `repr` attributes), but in terms of heap allocations, I&#x27;d argue there isn&#x27;t anything as ambiguous as a raw C++ pointer. If you&#x27;re able to get away with smart pointers all of the time, I could see this being less of an issue, but from my somewhat limited experience with C++ there seem to often be cases where APIs still expect raw pointers from time to time, so I wouldn&#x27;t expect to be able to look at some random function call in a call graph and know what type of memory it&#x27;s dealing with in the absence of documentation or runtime inspection.<p>In Rust, it&#x27;s a `Box&lt;T&gt;`, `Rc&lt;T&gt;`, `Arc&lt;T&gt;`, `Vec&lt;T&gt;`, or `String`, it&#x27;s on the heap. If it&#x27;s not, chances are it&#x27;s on the stack. There are separate types for the non-owning versions of those types for references (`&amp;T`), slices (`&amp;[T]`), and string references (`&amp;str)`, none of which require heap allocations to create (although they might indirectly refer to heap-allocated data in one of the other types mentioned before). There are probably other types that one might run into that are heap-allocated, but even when dealing with something like indirection from dynamic dispatch, any heap allocations needed to make things work will end up being explicit via something like `Box` or `Arc`. I might just be misunderstanding the point being made here; maybe the author was looking for documentation rather than relying on the types themselves, or maybe they had reason to be concerned about whether the type behind a reference or slice happened to be heap allocated or not, but in my experience, only needing to care about that in the context of when explicitly making a new allocation is a benefit, not a drawback.
    • jstimpfle41 days ago
      In C++, in particular when restricting to a C like subset, I prefer looking at an expression like<p><pre><code> foo-&gt;bar.baz </code></pre> instead of (in Rust and other modern languages that decided to get rid of the distinction)<p><pre><code> foo.bar.baz </code></pre> For example, the former lets me easily see that I can copy <i>foo-&gt;bar</i> and I now have a copy of <i>baz</i> (and indeed <i>bar</i>). In a newer language, it&#x27;s harder to see whether we are copying a value or a reference.
      • saghm41 days ago
        I see what you&#x27;re saying but I&#x27;d argue that this is mostly an unnecessary thing to worry about because with the exception of types explicitly opted into being cheaply copyable, you&#x27;re going to be moving it if you&#x27;re not accessing it via a reference. The idea is that if you&#x27;re worried about expensive copies, it shouldn&#x27;t be possible to copy implicitly in the first place; you&#x27;d either explicitly `clone` or you wouldn&#x27;t be copying it at all.
        • jstimpfle40 days ago
          I&#x27;m not worried about expensive copies. I&#x27;m worried about being able to understand my systems code. The solution isn&#x27;t adding more abstractions (like move semantics on top). I don&#x27;t want to move anything. I want to be clear about taking a reference or making an actual copy, these are deeply, semantically, different. This difference is important for single threaded code but also for concurrency -- not only with mutable data types but also with immutable ones.<p>Performance is mostly a consequence of clear and direct code. You mostly don&#x27;t achieve performance by saving individual copies, but by being in control of the code and architecture.
          • ibotty40 days ago
            I don&#x27;t think your run often into these things, because of Rust&#x27;s ownership enforcement. But I might be misunderstanding you, because it&#x27;s all pretty abstract and I might not have the whole context.
    • quadrophenia41 days ago
      OP here - thank you for your explanations! My writing got a little messy in this section, but I think my intended focus here was on struct alignment. I&#x27;ve only had to care about alignment in some very niche cases dealing with C, so I don&#x27;t think it&#x27;s something that&#x27;ll come up in my typical non-high performance software work. I agree that `repr` would likely be the way to go.<p>I do appreciate that the act of copying or allocating something nontrivial in Rust requires verbosity. Your list of typical heap containers is great and I&#x27;ll do my best to internalize that. I&#x27;d just add that some primitives like sync::Mutex [1] in Rust&#x27;s standard library (which to my knowledge don&#x27;t exist in the same flavor in C++&#x27;s STL) require some additional gymnastics to wrap my head around. The wrapper is super useful though :)<p>[1] <a href="https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;sync&#x2F;struct.Mutex.html" rel="nofollow">https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;sync&#x2F;struct.Mutex.html</a>
    • bobbylarrybobby41 days ago
      Is the following accurate? “If a type has a generic bound T: ?Sized, then its data lives on the heap.” (Except for &amp;T because it&#x27;s the one type that doesn&#x27;t take ownership of its T upon construction.)
      • nrds41 days ago
        There are no types whose fields &quot;live on the heap&quot;, nor are there types whose fields &quot;live on the stack&quot;; these are simply not properties of types. Values always live exactly where you put them and you can put values anywhere you want, thanks to Rust&#x27;s &quot;all types are moveable&quot; rule. Now something like a `Vec` or a `Box` _owns_ some data strictly on the heap, but that data is not _part of_ (i.e. a field of) the `Vec` or `Box` value.<p>As a counter-example to your idea, it&#x27;s theoretically possible for a type to have a `?Sized` field (at the end), although this idea was never completely fleshed out in the language. A value of such a type could be constructed on the stack.<p>Now in practice, if you encounter a type with an unsized type parameter, it&#x27;s probably a smart pointer. It may have an ownership relation to some data which lives on the heap. That may be what you&#x27;re referring to. But such heuristics are going to be more confusing than helpful for anyone who doesn&#x27;t understand the basic premise. The location of data in rust is actually quite simple, but sometimes beginners make it more complicated than it really is somehow.
  • epage41 days ago
    &gt; It may have been nice to expose some reasonable defaults for code coverage measurements too.<p>Would love built in coverage support but investigation is needed on the design (<a href="https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;cargo&#x2F;issues&#x2F;13040" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;cargo&#x2F;issues&#x2F;13040</a>) and we likely need to redo how we handle doctests (<a href="https:&#x2F;&#x2F;blog.rust-lang.org&#x2F;inside-rust&#x2F;2025&#x2F;10&#x2F;01&#x2F;this-development-cycle-in-cargo-1.90&#x2F;#all-hands-doctests" rel="nofollow">https:&#x2F;&#x2F;blog.rust-lang.org&#x2F;inside-rust&#x2F;2025&#x2F;10&#x2F;01&#x2F;this-devel...</a>).
  • justatdotin41 days ago
    &quot;Fortunately, I’m not that smart.&quot; - love that attitude.
  • dionian39 days ago
    Love the TUI idea for opensnitch. Fun, and practical.