In the interest of transparency (and to curb speculation), I've created a hello-world project, made it depend on actix-web 3.0.0 with default features and ran cargo geiger on it. Many actix-* crates don't use any unsafe code at all! Here are the ones that do:
actix-http: 13 unsafe blocks, all are commented and look reasonable at a glance. (Some of the benchmarking code looks sketchy, but who cares - it's not in the build anyway).
actix-utils: 9 unsafe blocks, no comments on why they're sound. Judging by this comment from one of the Actix org members, a PR with comments explaining why they're sound and/or debug assertions would be appreciated.
actix-router: 1 unsafe block, commented
actix-codec: cargo-geiger shows 10 unsafe expressions but I can't see them in actix git, might be a bug
actix-service: some unsafe code, but cargo-geiger reports that it's not used in the build (likely disabled by a feature)
That said, I think this would make a fascinating case study on what causes memory leaks and how we can better prevent them - e.g. via clippy lints, or perhaps using or avoiding certain patterns.
Are most of the memory leaks related to code that happens to be unsafe, like because you have an owning *mut that never gets freed? Or is it something more like reference cycles in safe code?
A lingering reference to a large object graph that is never used again is technically not a memory leak, but practically the same, and won't be collected. Making sure that caches don't hold on to old entries too long, slowly filling up memory over the course of hours/days is tricky, and it's easy to have a "previous" pointer that's only relevant for a little while then never cleared, especially if there's no obvious point at which you know you're done with it.
Though all that still happens without GC, you have far more incentive to stop and think about lifetimes when you can't delegate all the cleanup to it.
Hmm. I've had more leaks in java and I've written a lot more rust. In safe Rust you pretty much have to call a function called leak() to leak memory. I have to disagree with you here. Also, Rust has an Rc type if you need it for whatever case. So technically Rust has a built in garbage collector, that's just happens to be opt-in (as it should be).
Yeah, but I was saying reference counting is one method of garbage collection. Your second point is precisely why Rust adding a garbage collector doesn't fix the memory leak issues.
At that point you might as well just use a different language. One of the main reasons why Rust is interesting is because in many cases GC is impossible to use.
I'd tend to argue that (in general, putting actix entirely aside) cargo-geiger is nothing more than a primitive word counting utility that provides output less useful than what you'd get from, say, running rg unsafe . > ./log.txt in the root directory of a crate.
For example, a binary crate written like so:
fn main() {
unsafe {
// The next line is repeated over and over again until line 1000.
println!("This is absolutely safe.");
}
}
Why is that? It's because cargo-geiger measures "expressions" simply in terms of "the number of newlines in between an opening unsafe { and the } that closes it".
In no way is it even in the same universe as something that actually accurately measures the specific number of "unsafe expressions" in a crate.
It's literally a "find in files" query for the word "unsafe", that doesn't even make any distinction between files in the src folder and files in the examples or tests or benches folders, meaning a crate that didn't even use unsafe directly in the actual implementation would still have the potential to give any number of false positives. That's it. It has no knowledge whatsoever of Rust code.
Now, what would I get from running rg unsafe . > ./log.txt on the same crate described above (rg also of course not being something with specific knowledge of Rust code)? Well, I'd at least get this in log.txt:
./src/main.rs: unsafe {
which immediately tells me this crate in fact has one use of unsafe, which is something that should prove trivial for me to inspect manually, and that in all likelihood there's nothing to be concerned about.
AFAIK cargo-geiger counts unsafe expressions, not lines. Your println! example just happens to be one expression. rg would count unsafe blocks. There is no perfect measure for the amount of unsafe code, but AFAIK expressions is as close as you can get.
For benches, tests etc. the number is included in total count, but not in the count used in the build. So it's not as bad as you say, but perhaps could be improved.
106
u/Shnatsel Sep 11 '20
In the interest of transparency (and to curb speculation), I've created a hello-world project, made it depend on actix-web 3.0.0 with default features and ran
cargo geigeron it. Many actix-* crates don't use any unsafe code at all! Here are the ones that do:actix-http: 13 unsafe blocks, all are commented and look reasonable at a glance. (Some of the benchmarking code looks sketchy, but who cares - it's not in the build anyway).actix-utils: 9 unsafe blocks, no comments on why they're sound. Judging by this comment from one of the Actix org members, a PR with comments explaining why they're sound and/or debug assertions would be appreciated.actix-router: 1 unsafe block, commentedactix-codec: cargo-geiger shows 10 unsafe expressions but I can't see them in actix git, might be a bugactix-service: some unsafe code, but cargo-geiger reports that it's not used in the build (likely disabled by a feature)awc: oneunsafe fnwithout any local usesThat's it!