How to change a value while dropping? - rust

I’m currently trying to understand how drop works. The following code crashes and I don’t understand why. From my understanding, the usage of std::ptr::write should prevent the destructor (edit: of the original value, here: Rc) from running (in this case nothing bad should happen beside the memory leak). But it doesn’t seem to do that as (playpen, compile with -O0)
use std::rc::Rc;
use std::mem;
use std::ptr;
enum Foo {
Bar(Rc<usize>),
Baz
}
use Foo::*;
impl Drop for Foo {
fn drop(&mut self) {
match *self {
Bar(_) => {
unsafe { ptr::write(self, Foo::Baz) }
//unsafe { mem::forget(mem::replace(self, Foo::Baz)) }
}
Baz => ()
}
}
}
fn main() {
let _ = Foo::Bar(Rc::new(23));
}
gives an overflow error:
thread '<main>' panicked at 'arithmetic operation overflowed', /Users/rustbuild/src/rust-buildbot/slave/nightly-dist-rustc-mac/build/src/liballoc/rc.rs:755
The other variant quits with an illegal instruction. Why does that happen? How can I replace self with a value that will be properly dropped?

I'm not sure yet how to accomplish your goal, but I can show that
From my understanding, the usage of std::ptr::write should prevent the destructor from running
isn't true:
use std::mem;
use std::ptr;
struct Noisy;
impl Drop for Noisy {
fn drop(&mut self) { println!("Dropping!") }
}
enum Foo {
Bar(Noisy),
Baz
}
use Foo::*;
impl Drop for Foo {
fn drop(&mut self) {
println!("1");
match self {
&mut Bar(_) => {
println!("2");
unsafe { ptr::write(self, Foo::Baz) }
println!("3");
}
&mut Baz => {
println!("4");
}
}
println!("5");
}
}
fn main() {
let _ = Foo::Bar(Noisy);
}
This prints:
1
2
3
5
Dropping!
Indicating that the destructor for Foo::Bar is still being run, including the destructor of Noisy.
A potential solution is to use Option::take:
use std::mem;
struct Noisy;
impl Drop for Noisy {
fn drop(&mut self) { println!("Dropping!") }
}
enum Foo {
Bar(Option<Noisy>),
Baz
}
impl Drop for Foo {
fn drop(&mut self) {
match *self {
Foo::Bar(ref mut x) => {
unsafe { mem::forget(x.take()) }
}
Foo::Baz => {}
}
}
}
fn main() {
let _ = Foo::Bar(Some(Noisy));
}

Related

Allowing extension (beyond the crate) of implementation with event loop

Within the crate we can happily do something like this:
mod boundary {
pub struct EventLoop;
impl EventLoop {
pub fn run(&self) {
for _ in 0..2 {
self.handle("bundled");
self.foo();
}
}
pub fn handle(&self, message: &str) {
println!("{} handling", message)
}
}
pub trait EventLoopExtend {
fn foo(&self);
}
}
use boundary::EventLoopExtend;
impl EventLoopExtend for boundary::EventLoop {
fn foo(&self) {
self.handle("extended")
}
}
fn main() {
let el = boundary::EventLoop{};
el.run();
}
But if mod boundary were a crate boundary we get error[E0117]: only traits defined in the current crate can be implemented for arbitrary types.
I gather that a potential solution to this could be the New Type idiom, so something like this:
mod boundary {
pub struct EventLoop;
impl EventLoop {
pub fn run(&self) {
for _ in 0..2 {
self.handle("bundled");
self.foo();
}
}
pub fn handle(&self, message: &str) {
println!("{} handling", message)
}
}
pub trait EventLoopExtend {
fn foo(&self);
}
impl EventLoopExtend for EventLoop {
fn foo(&self) {
self.handle("unimplemented")
}
}
}
use boundary::{EventLoop, EventLoopExtend};
struct EventLoopNewType(EventLoop);
impl EventLoopExtend for EventLoopNewType {
fn foo(&self) {
self.0.handle("extended")
}
}
fn main() {
let el = EventLoopNewType(EventLoop {});
el.0.run();
}
But then the problem here is that the extended trait behaviour isn't accessible from the underlying EventLoop instance.
I'm still quite new to Rust, so I'm sure I'm missing something obvious, I wouldn't be surprised if I need to take a completely different approach.
Specifically in my case, the event loop is actually from wgpu, and I'm curious if it's possible to build a library where end users can provide their own "render pass" stage.
Thanks to #AlexN's comment I dug deeper into the Strategy Pattern and found a solution:
mod boundary {
pub struct EventLoop<'a, T: EventLoopExtend> {
extension: &'a T
}
impl<'a, T: EventLoopExtend> EventLoop<'a, T> {
pub fn new(extension: &'a T) -> Self {
Self { extension }
}
pub fn run(&self) {
for _ in 0..2 {
self.handle("bundled");
self.extension.foo(self);
}
}
pub fn handle(&self, message: &str) {
println!("{} handling", message)
}
}
pub trait EventLoopExtend {
fn foo<T: EventLoopExtend>(&self, el: &EventLoop<T>) {
el.handle("unimplemented")
}
}
}
use boundary::{EventLoop, EventLoopExtend};
struct EventLoopExtension;
impl EventLoopExtend for EventLoopExtension {
fn foo<T: EventLoopExtend>(&self, el: &EventLoop<T>) {
el.handle("extended")
}
}
fn main() {
let el = EventLoop::new(&EventLoopExtension {});
el.run();
}
The basic idea is to use generics with a trait bound. I think the first time I looked into this approach I was worried about type recursion. But it turns out passing the EventLoop object as an argument to EventLoopExtend trait methods is perfectly reasonable.

Closure lifetime in Rust through simple pattern

I'm trying to recreate a simple callback pattern in Rust using 2 structs. One will pass a bit of logic to execute whenever the other one is ready. The issue here is that the logic will run only if a certain value from the struct is true.
I can understand why the reference to Foo needs to live for 'static in this case, but I'm not sure how to refactor so that it compiles.
Seems like a pretty regular use case, but maybe I'm missing something since I'm new to Rust.
struct Foo {
value: bool,
}
struct Bar {
closure: Box<dyn Fn() -> ()>,
}
impl Foo {
fn new() -> Self {
Foo {
value: false,
}
}
fn set_closure(&self, b: &mut Bar) {
b.closure = self.get_closure();
}
fn get_closure(&self) -> Box<dyn Fn() -> ()> {
return Box::new(|| {
if self.value {
println!("true");
} else {
println!("false");
}
});
}
}
impl Bar {
fn new() -> Self {
Bar {
closure: Box::new(|| {})
}
}
}
fn main() {
let foo = Foo::new();
let mut bar = Bar::new();
foo.set_closure(&mut bar);
let closure = bar.closure;
closure();
}
Playground link -> here
You need to move the value into the closure:
fn get_closure(&self) -> Box<dyn Fn() -> ()> {
let value = self.value;
Box::new(move || {
if value {
println!("true");
} else {
println!("false");
}
})
}
Notice that in your example value is a bool which is Copy. If not you can either capture a reference or clone it. (If capturing a reference you may need to adjust some lifetimes there).
Playground

How to create a single threaded singleton in Rust?

I'm currently trying to wrap a C library in rust that has a few requirements. The C library can only be run on a single thread, and can only be initialized / cleaned up once on the same thread. I want something something like the following.
extern "C" {
fn init_lib() -> *mut c_void;
fn cleanup_lib(ctx: *mut c_void);
}
// This line doesn't work.
static mut CTX: Option<(ThreadId, Rc<Context>)> = None;
struct Context(*mut c_void);
impl Context {
fn acquire() -> Result<Rc<Context>, Error> {
// If CTX has a reference on the current thread, clone and return it.
// Otherwise initialize the library and set CTX.
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe { cleanup_lib(self.0); }
}
}
Anyone have a good way to achieve something like this? Every solution I try to come up with involves creating a Mutex / Arc and making the Context type Send and Sync which I don't want as I want it to remain single threaded.
A working solution I came up with was to just implement the reference counting myself, removing the need for Rc entirely.
#![feature(once_cell)]
use std::{error::Error, ffi::c_void, fmt, lazy::SyncLazy, sync::Mutex, thread::ThreadId};
extern "C" {
fn init_lib() -> *mut c_void;
fn cleanup_lib(ctx: *mut c_void);
}
#[derive(Debug)]
pub enum ContextError {
InitOnOtherThread,
}
impl fmt::Display for ContextError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ContextError::InitOnOtherThread => {
write!(f, "Context already initialized on a different thread")
}
}
}
}
impl Error for ContextError {}
struct StaticPtr(*mut c_void);
unsafe impl Send for StaticPtr {}
static CTX: SyncLazy<Mutex<Option<(ThreadId, usize, StaticPtr)>>> =
SyncLazy::new(|| Mutex::new(None));
pub struct Context(*mut c_void);
impl Context {
pub fn acquire() -> Result<Context, ContextError> {
let mut ctx = CTX.lock().unwrap();
if let Some((id, ref_count, ptr)) = ctx.as_mut() {
if *id == std::thread::current().id() {
*ref_count += 1;
return Ok(Context(ptr.0));
}
Err(ContextError::InitOnOtherThread)
} else {
let ptr = unsafe { init_lib() };
*ctx = Some((std::thread::current().id(), 1, StaticPtr(ptr)));
Ok(Context(ptr))
}
}
}
impl Drop for Context {
fn drop(&mut self) {
let mut ctx = CTX.lock().unwrap();
let (_, ref_count, ptr) = ctx.as_mut().unwrap();
*ref_count -= 1;
if *ref_count == 0 {
unsafe {
cleanup_lib(ptr.0);
}
*ctx = None;
}
}
}
I think the most 'rustic' way to do this is with std::sync::mpsc::sync_channel and an enum describing library operations.
The only public-facing elements of this module are launch_lib(), the SafeLibRef struct (but not its internals), and the pub fn that are part of the impl SafeLibRef.
Also, this example strongly represents the philosophy that the best way to deal with global state is to not have any.
I have played fast and loose with the Result::unwrap() calls. It would be more responsible to handle error conditions better.
use std::sync::{ atomic::{ AtomicBool, Ordering }, mpsc::{ SyncSender, Receiver, sync_channel } };
use std::ffi::c_void;
extern "C" {
fn init_lib() -> *mut c_void;
fn do_op_1(ctx: *mut c_void, a: u16, b: u32, c: u64) -> f64;
fn do_op_2(ctx: *mut c_void, a: f64) -> bool;
fn cleanup_lib(ctx: *mut c_void);
}
enum LibOperation {
Op1(u16,u32,u64,SyncSender<f64>),
Op2(f64, SyncSender<bool>),
Terminate(SyncSender<()>),
}
#[derive(Clone)]
pub struct SafeLibRef(SyncSender<LibOperation>);
fn lib_thread(rx: Receiver<LibOperation>) {
static LIB_INITIALIZED: AtomicBool = AtomicBool::new(false);
if LIB_INITIALIZED.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst).is_err() {
panic!("Tried to double-initialize library!");
}
let libptr = unsafe { init_lib() };
loop {
let op = rx.recv();
if op.is_err() {
unsafe { cleanup_lib(libptr) };
break;
}
match op.unwrap() {
LibOperation::Op1(a,b,c,tx_res) => {
let res: f64 = unsafe { do_op_1(libptr, a, b, c) };
tx_res.send(res).unwrap();
},
LibOperation::Op2(a, tx_res) => {
let res: bool = unsafe { do_op_2(libptr, a) };
tx_res.send(res).unwrap();
}
LibOperation::Terminate(tx_res) => {
unsafe { cleanup_lib(libptr) };
tx_res.send(()).unwrap();
break;
}
}
}
}
/// This needs to be called no more than once.
/// The resulting SafeLibRef can be cloned and passed around.
pub fn launch_lib() -> SafeLibRef {
let (tx,rx) = sync_channel(0);
std::thread::spawn(|| lib_thread(rx));
SafeLibRef(tx)
}
// This is the interface that most of your code will use
impl SafeLibRef {
pub fn op_1(&self, a: u16, b: u32, c: u64) -> f64 {
let (res_tx, res_rx) = sync_channel(1);
self.0.send(LibOperation::Op1(a, b, c, res_tx)).unwrap();
res_rx.recv().unwrap()
}
pub fn op_2(&self, a: f64) -> bool {
let (res_tx, res_rx) = sync_channel(1);
self.0.send(LibOperation::Op2(a, res_tx)).unwrap();
res_rx.recv().unwrap()
}
pub fn terminate(&self) {
let (res_tx, res_rx) = sync_channel(1);
self.0.send(LibOperation::Terminate(res_tx)).unwrap();
res_rx.recv().unwrap();
}
}

Rust matching and borrow checker

I keep stumbling on a pattern in my Rust programs that always puts me at odds with the borrow-checker. Consider the following toy example:
use std::sync::{Arc,RwLock};
pub struct Test {
thing: i32,
}
pub struct Test2 {
pub test: Arc<RwLock<Test>>,
pub those: i32,
}
impl Test {
pub fn foo(&self) -> Option<i32> {
Some(3)
}
}
impl Test2 {
pub fn bar(&mut self) {
let mut test_writer = self.test.write().unwrap();
match test_writer.foo() {
Some(thing) => {
self.add(thing);
},
None => {}
}
}
pub fn add(&mut self, addme: i32) {
self.those += addme;
}
}
This doesn't compile because the add function in the Some arm tries to borrow self mutably, which was already borrowed immutably just above the match statement in order to open the read-write lock.
I've encountered this pattern a few times in Rust, mainly when using RwLock. I've also found a workaround, namely by introducing a boolean before the match statement and then changing the value of the boolean in the Some arm and then finally introducing a test on this boolean after the match statement to do whatever it is I wanted to do in the Some arm.
It just seems to me that that's not the way to go about it, I assume there's a more idiomatic way to do this in Rust - or solve the problem in an entirely different way - but I can't find it. If I'm not mistaken the problem has to do with lexical borrowing so self cannot be mutably borrowed within the arms of the match statement.
Is there an idiomatic Rust way to solve this sort of problem?
Use directly the field those, for example with custom type:
use std::sync::{Arc,RwLock};
pub struct Those(i32);
impl Those {
fn get(&self) -> i32 {
self.0
}
fn add(&mut self, n: i32) {
self.0 += n;
}
}
pub struct Test {
thing: Those,
}
pub struct Test2 {
pub test: Arc<RwLock<Test>>,
pub those: Those,
}
impl Test {
pub fn foo(&self) -> Option<Those> {
Some(Those(3))
}
}
impl Test2 {
pub fn bar(&mut self) {
let mut test_writer = self.test.write().unwrap();
match test_writer.foo() {
Some(thing) => {
// call a method add directly on your type to get around the borrow checker
self.those.add(thing.get());
},
None => {}
}
}
}
You either need to end borrow of a part of self, before mutating self
pub fn bar1(&mut self) {
let foo = self.test.write().unwrap().foo();
match foo {
Some(thing) => {
self.add(thing);
},
None => {}
}
}
or directly mutate non borrowed part of self
pub fn bar2(&mut self) {
let test_writer = self.test.write().unwrap();
match test_writer.foo() {
Some(thing) => {
self.those += thing;
},
None => {}
}
}

Access to self from the parameters of a macro that creates a struct

I'm trying to write a macro that generates a struct. The implementation for the struct will be generated by the macro, but some blocks of code will be provided as macro arguments. Here is a minimal example of such a macro:
macro_rules! make_struct {
($name:ident $block:block) => {
struct $name {
foo: i32,
}
impl $name {
fn new() -> Self {
$name { foo: 42 }
}
fn act (&self) {
$block
}
}
};
}
And here is an example of how the macro can be used:
fn main() {
// Works
make_struct!(Test { println! ("Bar: {:?}", 24); });
let test = Test::new();
test.act();
}
I would like to give access to self inside the supplied code. Something like:
fn main() {
// Does not work
make_struct!(Test { println! ("Foo: {:?}", self.foo); });
let test = Test::new();
test.act();
}
I understand that this does not work because of macro hygiene rules. Specifically, the self.foo expression is evaluated in the syntax context of the main function, where self does not exist. The question is: is there a way to modify the macro so that self may be accessed from the user-supplied code?
Code on playground
You can pass a closure instead of a block.
make_struct!(Test |this| println!("Foo: {:?}", this.foo));
Then the macro can use the closure and call it with self:
macro_rules! make_struct {
($name:ident $closure:expr) => {
struct $name {
foo: i32,
}
impl $name {
fn new() -> Self {
$name { foo: 42 }
}
fn act (&self) {
let x: &Fn(&Self) = &$closure;
x(self)
}
}
};
}
The dance with the let binding is necessary, because the type of this in the closure can't be inferred (yet?). And it also makes your macro's error reporting a little more readable when something other than a closure is passed.
Found a way to do it by adding a parameter to the macro that stores the name by which self will be accessed in the blocks:
macro_rules! make_struct {
($myname:ident : $type_name:ident $block:block) => {
struct $type_name {
foo: i32,
}
impl $type_name {
fn new() -> Self {
$type_name { foo: 42 }
}
fn act (&self) {
let $myname = self;
$block
}
}
};
}
fn main() {
make_struct!(myself: Test { println! ("Foo: {:?}", myself.foo); });
let test = Test::new();
test.act();
}

Resources