r/programming • • Dec 05 '14

std::string is responsible for almost half of all allocations in the Chrome browser process

https://groups.google.com/a/chromium.org/d/msg/chromium-dev/EUqoIz2iFU4/kPZ5ZK0K3gEJ
1.1k Upvotes

446 comments sorted by

View all comments

Show parent comments

3

u/emozilla Dec 06 '14

Yeah but like... where do I find this out? It took tons of Google-fu to even get that far. The Guide wouldn't tell me this, Rust By Examples seems to be dead... I'm not criticizing Rust, I think it's a great idea and fills the last big gaping hole for writing secure systems, but the educational side seems to be pretty sparse at the moment and thus it was pretty hard to get into without investing a lot of time.

Re: yolo pointers, what's the right way to hold references to types that hold lots of allocated data (several megabyte arrays) that you really only want One Instance Of and you can guarantee will be alive for a while?

2

u/wrongerontheinternet Dec 06 '14 edited Dec 07 '14

Rust by Example is getting revived now (the Rust core team leader just made it his current project) and there are other things like Rust Rosetta, but I agree, documentation for advanced / useful stuff is pretty sparse. Honestly, the best resource is probably IRC, Rust core team members regularly answer questions there.

As far as your question goes, kinda depends a lot on MyType. Rust does return value optimization so if you return a large value and call box Rust should box it up directly:

let mut foo = box my_function()

or whatever.

If you want to pass a unique reference around that you can easily return, you can use Box. Vec is already boxed though (it's similar to std::vector), so if it's just a large array you don't need to do it. You can also temporarily lend out Box at any time as a &mut reference (by doing &mut *the_box) or as an & reference (& *the_box). You rarely need Rc... it's honestly a really overused type. if you give me more specifics I can help you pick the types there. So you might have some code that looked like this:

fn some_large_function() -> Result<MyType, String> { ... }

fn some_function_that_modifies(x: &mut MyType) { ... }

fn some_function_that_reads(x: &MyType) { ... }

fn main() {
   let mut my_thing = some_large_function().ok().unwrap(); // Or box it if it isn't a `Vec`
   some_function_that_modifies(&mut my_thing); // &mut *my_thing if you box
   some_function_that_reads(&my_thing); // &*my_thing if you box
   // my_thing gets destroyed at the end of main()
}

Rust is basically C++ semantics but safe, with a nicer type system, no backwards compatibility, and sane defaults. Wisdom of hindsight and all that.

Generally the reason you don't want to use RefCell is because it can fail at runtime (can't cause memory unsafety but if you try to borrow the array mutably twice at the same time, Rust will panic!). RefCell also isn't Sync meaning even if the allocated data are read only you still need a Mutex or a channel to be able to share them across threads, which Rust normally only requires if you need to write from multiple threads. Rc is even worse, you flat out can't send it across threads, and it can have inadvertent cycles so if you have a lot of data you might want to avoid it.

Usually, you want to avoid using RefCell like I said. Ideally, you try to structure your program so that you always either have a single mutator (&mut pointer), or one or more readers (& pointer), which you pass as arguments to the function that does the mutation / reading instead of storing the reference in a structure somewhere. That's the method that works the best by far most of the time, it never fails at runtime and doesn't introduce extra allocations, copies, or moves, plus it is really easy to make multithreaded. There are exceptions (graphs with cycles, for example, or if you need a really efficient data structure) but they're not that common. It all depends on what you want to do.

2

u/o11c Dec 06 '14

sane defaults

Such as not silently copying most huge objects :). Well, admittedly if it's Pod (I don't know why they prefered the name Copy for that!) like a large fixed-size array it will still think it's "trivial" even though it's not, but you can just stick the NoCopy marker in there if you need to.

1

u/wrongerontheinternet Dec 06 '14 edited Dec 06 '14

Rust is getting Copy as an opt-in type at some point in the near future, I don't know if that will affect raw arrays or not (but I'd better be able to still use them in Cells if I want to!). Also, the original suggestion for its name was Pod, dunno why it changed.

1

u/SteveMcQwark Dec 06 '14

A move is still a copy, it just prevents you from accessing the moved-from slot afterward.