Why reference counting exist..when reference counting is needed or efficient to use?

why reference counting exist..when reference counting is needed or efficient to use ?

Reference counting is useful when you need shared ownership of something and it's impossible to know how long the thing will be alive for at compile time.

For example, say I want to share a flag between the main thread and a background thread.

use std::sync::{Arc, atomic::{AtomicBool, Ordering}};

let cancel_requested = Arc::new(AtomicBool::new(false));

// Get a second reference to the flag and pass it to a background thread
let cancel_requested_2 = Arc::clone(&flag); 
std::thread::spawn(move || {
  // Keep doing some work until the flag is set
  while !cancel_requested_2.load(Ordering::Relaxed) {
    do_some_work_in_the_background();
  }
});

println!("Press enter to stop doing work.");
let mut buffer = String::new();
std::io::stdin().read_line(&mut buffer)?;

// the user has hit enter so our background loop should finish
cancel_requested.store(true, Ordering::Relaxed);

It's a bit of a toy example, but because a background thread can theoretically outlive the main thread, there's no way to know how long our cancel_requested flag will live at compile time. That means you can't (in general) use lifetimes to avoid use-after-free issues.

4 Likes

Here's a simple example in rusty-looking pseudo-code.

// value is some kinda value we might want to use
let value = NodeValue::new();
// list has a reference to value
list.add(&value);
// tree also has a reference to value
tree.add(&value);

// pre_filter can mutate the list, 
// the list may or may not contain ref to value afterward
list.pre_filter();
// Now we mutate tree too!
tree.drop_leaf();


// More Code goes here
// Can't do this! list or tree might still have a ref to value!
drop(value);

In the example above since we can't know whether the references to value have been dropped, we can't drop value. Value needs to continue wasting memory until both tree and list have been dropped even if neither of them actually has the reference anymore. That could be the entire time the program is running.

You can use reference counting via Rc to solve this problem. Rc will let value be dropped the moment both tree and list have dropped their references.

// value is some kinda value we might want to use
let value = Rc::new(NodeValue::new());
// list has the value, there are now 2 references
list.add(value.clone());
// tree also the value, it takes the original reference
// so there are still 2 references.
tree.add(value);

// pre_filter can mutate the list, 
// the list may or may not contain ref to value afterward
list.pre_filter();
// Now we mutate tree too!
tree.drop_leaf();

// More Code goes here

In the example above, by the time you get to "More Code"

  • value may be dropped (not in either tree or list)
  • value may exist only in list
  • value may exist only in tree
  • value may exist in both tree and list

Rc is managing this for us at runtime since we really can't know at compile-time which of the 4 scenarios above will be true.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.