#[macro_export]
macro_rules! fn_range {
($v: ident, $f: expr) => {
match $v {
Value::Range(r) => {
Value::from_f64($f(r))
}
Value::String(s) => match Decimal::from_str(&s) {
Ok(d) => Value::Decimal(d),
Err(_) => nan(),
},
_ => $v.clone(),
}
}
}
#[inline]
pub fn sum(v: Value) -> Value {
fn_range!(v, r.iter_f64().sum()) // not found in this scope
}
that syntax expects $f
to be a callable taking a parameter, but in your case $f
is r.iter_f64().sum()
so this expands to (r.iter_f64().sum())(r)
- with the added complexity that the
$f
-expandedr
does not know about the macro-hiddenr
variable (this is called macro hygiene).
The solution to both issues is simple: use a closure.
- fn_range!(v, r.iter_f64().sum())
+ fn_range!(v, |r| r.iter_f64().sum())
(and to be clear, the closure could have been using a name other than r
, such as:
|it| it.iter_f64().sum()
)
@Yandros thanks for reply. I was thinking to use closure :).
@Yandros tried to use closure, but got consider giving this closure parameter a type
. Can I avoid specifying type?
Oh, yes, closures can be annoying because of that.
Generally, there may be times where you need a type annotation and there isn't much you can do about it. Other times, you may work around that limitation with type annotations elsewhere.
- For instance, if your closure captures no environment, you could have the macro expand to:
=> { let f: fn(<type annotation here>) -> _ = $f; Value::from_f64(f(r)) }
But luckily, in this case, we can go and transform a bit the macro to work like I think you originally intended:
#[macro_export]
macro_rules! fn_range {(
$v:expr,
|$r:ident| $fr:expr $(,)?
) => (
match $v {
| Value::Range($r) => {
Value::from_f64($fr)
},
| Value::String(s) => match Decimal::from_str(&s) {
| Ok(d) => Value::Decimal(d),
| Err(_) => nan(),
},
| ref v => v.clone(),
}
)}
#[inline]
pub fn sum(v: Value) -> Value {
fn_range!(v, |r| r.iter_f64().sum()) // not found in this scope
}
Basically the trick is to take your initial syntax $v, $fr
, with an expansion to $fr
directly instead of $f(r)
, but that would struggle because of the aforementioned hygiene: the macro binds the contents of Value::Range
to a variable it names r
, and such name is not visible nor usable by the caller of the macro (this helps a lot of "stupid bugs" that we can have, for instance, with C ands its "dumb" preprocessor).
The solution, in that case, is for the caller of the macro to provide the variable name that the macro will use internally:
fn_range!(v, r, r.iter_f64().sum())
^ ^^^^^^^^^^^^^^^^^^
var expansion
- This expansion has now no ambiguity w.r.t, the type of
r
.
And at that point, since a macro chooses the syntax with which to parse its input, I have changed that to use a closure-looking syntax;
fn_range!(v, |r| r.iter_f64().sum())
^ ^^^^^^^^^^^^^^^^^
var expansion
@Yandros wonderful, this works. thanks for detailed reply
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.