From 7a02106cb9014fc5a4df7906cd10f76e1b267b0d Mon Sep 17 00:00:00 2001 From: Mat Sadler Date: Tue, 22 Aug 2023 21:33:14 -0700 Subject: [PATCH] pass &Ruby handle to functions used as Ruby blocks --- CHANGELOG.md | 2 ++ src/block.rs | 23 ++++++++++++----------- src/error.rs | 10 ++-------- src/method.rs | 7 ++++--- src/value.rs | 9 +++++---- tests/block_call.rs | 2 +- tests/proc_new.rs | 2 +- 7 files changed, 27 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e9ec004..1d9bd04c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ inaccessible to Rust. ### Changed +- Closures/Functions used as Ruby blocks/procs take an additional first + argument of `&Ruby`. ### Deprecated diff --git a/src/block.rs b/src/block.rs index 0aa46e49..2db0dfb0 100644 --- a/src/block.rs +++ b/src/block.rs @@ -52,7 +52,7 @@ impl Ruby { /// use magnus::{prelude::*, rb_assert, Error, Ruby}; /// /// fn example(ruby: &Ruby) -> Result<(), Error> { - /// let proc = ruby.proc_new(|args, _block| { + /// let proc = ruby.proc_new(|_ruby, args, _block| { /// let acc = i64::try_convert(*args.get(0).unwrap())?; /// let i = i64::try_convert(*args.get(1).unwrap())?; /// Ok(acc + i) @@ -66,7 +66,7 @@ impl Ruby { /// } /// # Ruby::init(example).unwrap() /// ``` - pub fn proc_new(&self, block: fn(&[Value], Option) -> R) -> Proc + pub fn proc_new(&self, block: fn(&Ruby, &[Value], Option) -> R) -> Proc where R: BlockReturn, { @@ -80,7 +80,8 @@ impl Ruby { where R: BlockReturn, { - let func = std::mem::transmute::) -> R>(callback_arg); + let func = + std::mem::transmute::) -> R>(callback_arg); func.call_handle_error(argc, argv as *const Value, Value::new(blockarg)) .as_rb_value() } @@ -107,7 +108,7 @@ impl Ruby { /// fn example(ruby: &Ruby) -> Result<(), Error> { /// let mut count = 0; /// - /// let proc = ruby.proc_from_fn(move |args, _block| { + /// let proc = ruby.proc_from_fn(move |_ruby, args, _block| { /// let step = i64::try_convert(*args.get(0).unwrap())?; /// count += step; /// Ok(count) @@ -123,7 +124,7 @@ impl Ruby { /// ``` pub fn proc_from_fn(&self, block: F) -> Proc where - F: 'static + Send + FnMut(&[Value], Option) -> R, + F: 'static + Send + FnMut(&Ruby, &[Value], Option) -> R, R: BlockReturn, { unsafe extern "C" fn call( @@ -134,7 +135,7 @@ impl Ruby { blockarg: VALUE, ) -> VALUE where - F: FnMut(&[Value], Option) -> R, + F: FnMut(&Ruby, &[Value], Option) -> R, R: BlockReturn, { let closure = &mut *(callback_arg as *mut F); @@ -211,7 +212,7 @@ impl Proc { /// use magnus::{block::Proc, prelude::*, rb_assert}; /// # let _cleanup = unsafe { magnus::embed::init() }; /// - /// let proc = Proc::new(|args, _block| { + /// let proc = Proc::new(|_ruby, args, _block| { /// let acc = i64::try_convert(*args.get(0).unwrap())?; /// let i = i64::try_convert(*args.get(1).unwrap())?; /// Ok(acc + i) @@ -226,7 +227,7 @@ impl Proc { deprecated(note = "please use `Ruby::proc_new` instead") )] #[inline] - pub fn new(block: fn(&[Value], Option) -> R) -> Self + pub fn new(block: fn(&Ruby, &[Value], Option) -> R) -> Self where R: BlockReturn, { @@ -251,7 +252,7 @@ impl Proc { /// /// let mut count = 0; /// - /// let proc = Proc::from_fn(move |args, _block| { + /// let proc = Proc::from_fn(move |_ruby, args, _block| { /// let step = i64::try_convert(*args.get(0).unwrap())?; /// count += step; /// Ok(count) @@ -268,7 +269,7 @@ impl Proc { #[inline] pub fn from_fn(block: F) -> Self where - F: 'static + Send + FnMut(&[Value], Option) -> R, + F: 'static + Send + FnMut(&Ruby, &[Value], Option) -> R, R: BlockReturn, { get_ruby!().proc_from_fn(block) @@ -468,7 +469,7 @@ impl TryConvert for Proc { /// dropped when the returned `Value` is garbage collected. fn wrap_closure(func: F) -> (*mut F, Value) where - F: FnMut(&[Value], Option) -> R, + F: FnMut(&Ruby, &[Value], Option) -> R, R: BlockReturn, { struct Closure(F, DataType); diff --git a/src/error.rs b/src/error.rs index a6e2c28c..9cdd36ec 100644 --- a/src/error.rs +++ b/src/error.rs @@ -55,15 +55,9 @@ impl Ruby { /// fn example(ruby: &Ruby) -> Result<(), Error> { /// let i: i64 = ruby /// .range_new(1, 100, false)? - /// .block_call("each", (), |args, _block| { + /// .block_call("each", (), |ruby, args, _block| { /// let i = i64::try_convert(*args.get(0).unwrap())?; /// if i % 3 == 0 && i % 5 == 0 { - /// // `block_call` takes a function pointer, not a - /// // closure, so can't capture `ruby`. As we know this - /// // will always be called from Ruby it's safe to get - /// // `Ruby` without the checks that we're on a Ruby - /// // thread. - /// let ruby = unsafe { Ruby::get_unchecked() }; /// Err(ruby.iter_break_value(i)) /// } else { /// Ok(()) @@ -171,7 +165,7 @@ impl Error { /// /// let i: i64 = magnus::Range::new(1, 100, false) /// .unwrap() - /// .block_call("each", (), |args, _block| { + /// .block_call("each", (), |_ruby, args, _block| { /// let i = i64::try_convert(*args.get(0).unwrap())?; /// if i % 3 == 0 && i % 5 == 0 { /// Err(Error::iter_break(i)) diff --git a/src/method.rs b/src/method.rs index 7ba21655..c483b19d 100644 --- a/src/method.rs +++ b/src/method.rs @@ -367,7 +367,7 @@ where #[doc(hidden)] pub trait Block where - Self: Sized + FnMut(&[Value], Option) -> Res, + Self: Sized + FnMut(&Ruby, &[Value], Option) -> Res, Res: BlockReturn, { #[inline] @@ -377,8 +377,9 @@ where argv: *const Value, blockarg: Value, ) -> Result { + let ruby = unsafe { Ruby::get_unchecked() }; let args = slice::from_raw_parts(argv, argc as usize); - (self)(args, Proc::from_value(blockarg)).into_block_return() + (self)(&ruby, args, Proc::from_value(blockarg)).into_block_return() } #[inline] @@ -398,7 +399,7 @@ where impl Block for Func where - Func: FnMut(&[Value], Option) -> Res, + Func: FnMut(&Ruby, &[Value], Option) -> Res, Res: BlockReturn, { } diff --git a/src/value.rs b/src/value.rs index da50a5c3..1557b865 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1254,7 +1254,7 @@ pub trait ReprValue: private::ReprValue { /// # let _cleanup = unsafe { magnus::embed::init() }; /// /// let values = eval::(r#"["foo", 1, :bar]"#).unwrap(); - /// let block = Proc::new(|args, _block| args.first().unwrap().to_r_string()); + /// let block = Proc::new(|_ruby, args, _block| args.first().unwrap().to_r_string()); /// let _: Value = values.funcall_with_block("map!", (), block).unwrap(); /// assert_eq!(values.to_vec::().unwrap(), vec!["foo", "1", "bar"]); /// ``` @@ -1310,7 +1310,7 @@ pub trait ReprValue: private::ReprValue { /// /// let values = eval::(r#"["foo", 1, :bar]"#).unwrap(); /// let _: Value = values - /// .block_call("map!", (), |args, _block| { + /// .block_call("map!", (), |_ruby, args, _block| { /// args.first().unwrap().to_r_string() /// }) /// .unwrap(); @@ -1320,7 +1320,7 @@ pub trait ReprValue: private::ReprValue { self, method: M, args: A, - block: fn(&[Value], Option) -> R, + block: fn(&Ruby, &[Value], Option) -> R, ) -> Result where M: IntoId, @@ -1338,7 +1338,8 @@ pub trait ReprValue: private::ReprValue { where R: BlockReturn, { - let func = std::mem::transmute::) -> R>(callback_arg); + let func = + std::mem::transmute::) -> R>(callback_arg); func.call_handle_error(argc, argv as *const Value, Value::new(blockarg)) .as_rb_value() } diff --git a/tests/block_call.rs b/tests/block_call.rs index c07d907b..c9ae4c83 100644 --- a/tests/block_call.rs +++ b/tests/block_call.rs @@ -10,7 +10,7 @@ fn it_can_call_method_with_block() { ruby.into_value(3_i64), ]); let _: Value = ary - .block_call("map!", (), |args, _| { + .block_call("map!", (), |_, args, _| { i64::try_convert(args[0]).map(|i| i * 4) }) .unwrap(); diff --git a/tests/proc_new.rs b/tests/proc_new.rs index dbe89ef1..8ef20691 100644 --- a/tests/proc_new.rs +++ b/tests/proc_new.rs @@ -3,7 +3,7 @@ use magnus::{block::Proc, eval, gc, value::Opaque, Ruby, Value}; fn make_proc(ruby: &Ruby) -> Proc { let x = String::from("foo"); let y = Opaque::from(ruby.str_new("bar")); - ruby.proc_from_fn(move |_args, _block| Ok((x.clone(), y))) + ruby.proc_from_fn(move |_ruby, _args, _block| Ok((x.clone(), y))) } #[test]