5 ms·
>Rusts solution is a more general solution to the "general resource problem" It's true. Ever since I got familiar with the ownership concept, I've taken to wr
by Mouse47 9y ago
>Rusts solution is a more general solution to the "general resource problem"
It's true. Ever since I got familiar with the ownership concept, I've taken to writing C# IDisposables with disposable member variables like this:
public MyObject(){
this.myResource = new DisposableResource();
this.ownsMyResource = true;
}
public MyObject(DisposableResource resourceToUseThatIDontOwn){
this.myResource = resourceToUseThatIDontOwn;
this.ownsMyResource = false;
}
public void Dispose(){
//not pictured: .NET's boilerplate dispose code
if(ownsMyResource) myResource.Dispose();
}
For the client I'm contracting with right now, mismanagement of disposable resources is the #1 issue in the codebase. 100k lines of code, and it's never clear who should be disposing connections. There are some objects with multiple constructors (like above) that have 'conditional' ownership. In Rust, it's impossible to have this problem, period...although the above C# construct simulates it, lol.
- cesarb 9y agoFun fact: what you did is a hand implementation of Rust's "drop flag" (https://doc.rust-lang.org/nomicon/drop-flags.html https://doc.rust-lang.org/nomicon/drop-flags.html).
- Mouse47 9y agoThat's pretty cool - it never occurred to me that determining drops required anything other than static analysis. Nice that it doesn't affect the layout of the types, either...which I'm guessing is why they added: >The drop flags are tracked on the stack and no longer stashed in types that implement drop.
- randomThoughts9 9y agoI agree and I appreciate what they are trying to do. But just as a counter-argument, look how a simple lock looks like in Rust [1]: fn main() { let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = counter.clone(); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } //... } [1] https://doc.rust-lang.org/book/second-edition/ch16-03-shared-state.html https://doc.rust-lang.org/book/second-edition/ch16-03-shared... ----- edit: Just to clarify my point. The ownership model is a beautiful solution to the resource management problem. And as the parent pointed out, some concepts can be cleanly borrowed/reused in other languages. But now, in Rust, in practice, you realize that this model requires a lot of boilerplate code just to make the borrow checker happy, even for simple things like using a lock: create Arc(Mutex(data)); clone the arc reference; move the new Arc reference to the new thread.
- pjmlp 9y agoYou can simulate something like that using lambdas in C#. Something like withLock(counter, (num) => { return num + 1; }); or in alternative, withLock(counter, (ref num) => { num++; }); If C# allowed for trailing lambdas it could look better.
- GlitchMr 9y agoThe issue really is lack of scoped threads forcing usage of reference counting to figure out when to drop a value. With crossbeam scoped threads implementation this looks much nicer. extern crate crossbeam; use std::sync::Mutex; fn main() { let counter = Mutex::new(0); crossbeam::scope(|scope| for _ in 0..10 { scope.spawn(|| *counter.lock().unwrap() += 1); }); println!("{:?}", counter); } Four lines to declare a mutex, start 10 threads that all lock that mutex, increase value behind mutex, unlock mutex, and join all spawned threads. There is a lot going on here (unwrap is arguably a noise here however, but the rest is straight to the point). Also, it's possible to use atomic ints here instead of mutex. Verbosity of mutex actually helps a bit, because locking is fairly expensive and it's better to avoid locking if possible.
- deleted 9y ago[deleted]