Props to Rust for tackling this head on, more languages should provide resource allocation that's deterministic, predictable and syntactically convenient.
CPython's behavior is nice, but it seems to me it came about by accident. Big heavy resources use refcounting because everything uses refcounting. Plus, if CPython had true concurrency, across-the-board refcounting probably wouldn't have lasted nearly as long (reason being: multithreaded refcounting requires atomic ops which make them much more expensive).
But in the general language design world, IMHO, refcounting is actually a pretty good compromise for resource management. Because:
1) The efficiency lost compared to Rust's approach is probably immaterial (since the Big Expensive Object that you're mananaging dwarfs the refcounting -- syscalls and file descriptors are expensive). So I don't think zero overhead is critical.
2) Cycles are pretty easy to avoid when you're only refcounting Big Expensive Objects (FDs, mmapped buffers, etc. etc.) For a general runtime, refcounting is tricky. But if it's a special part of the language that's small, simple, and only for resource management, it's pretty convenient. I think it's worlds better than the approach of mixing GC and resource management -- language designers should admit that's a terrible idea.