So I was writing (attempting) some variadic macros to try to implement compose and curry in rust. pipe was easy enough.
Variadic partial application though? Not so much. Props to anyone who can come up with a solution for this partial application macro that doesn't use those pesky seemingly ignorable type args (and maybe variadic? w/o reflection I think it's impossible):
//ltr compose for closures
macro_rules! pipe {
($init:tt |> $($fn:ident),+) => {{
let mut r = $init;
$( r = $fn(r); )*
r
}}
}
//partially apply a closure w/ up to 4 args
macro_rules! apply {
($fn:ident<$t1:ty,$t2:ty>($arg1:expr)) => {
|x:$t2| $fn($arg1 as $t1, x)
};
($fn:ident<$t1:ty,$t2:ty,$t3:ty>($arg1:expr)) => {
|x:$t2, y:$t3| $fn($arg1 as $t1,x, y)
};
($fn:ident<$t1:ty,$t2:ty,$t3:ty>($arg1:expr, $arg2:expr)) => {
|x:$t3| $fn($arg1 as $t1,$arg2 as $t2,x)
};
($fn:ident<$t1:ty,$t2:ty,$t3:ty,$t4:ty>($arg1:expr)) => {
|x: $t2, y:$t3, z:$t4| $fn($arg1 as $t1, x, y, z)
};
($fn:ident<$t1:ty,$t2:ty,$t3:ty,$t4:ty>($arg1:expr, $arg2:expr)) => {
|x:$t3, y:$t4| $fn($arg1 as $t1,$arg2 as $t2, x, y)
};
($fn:ident<$t1:ty,$t2:ty,$t3:ty,$t4:ty>($arg1:expr, $arg2:expr, $arg3:expr)) => {
|x:$t4| $fn($arg1 as $t1,$arg2 as $t2,arg3 as $t3,x)
};
}
fn main() {
let add = |x:i32, y:i32| x + y;
let sq = |x:i32| x * x;
let dbl = |x:i32| x * 2;
//blargh i hate those typeargs! i need them for the matcher
//to get the original params number and type since rust has no reflection :(
//weirdly if i ignore them with _ it works. But it will luckily fail
//hard if you supply incorrect args.
let plus1 = apply!(add<_,_>(1));
let r = pipe!(3 |> dbl, plus1, sq);
print!("{}", r);
}
Edit: The reason I seemingly cannot do variadic args on this is because that changes the structure of the closure. Seemed fine at first glance, I'd just wrap it in a match statement based on the total params and difference between the number of exprs vs total params. but if you try to return closures of varying signatures in a match arm it won't compile. The good news is this approach is probably low-overhead. But its really not a great improvement over partial application thru inline closures as necessary:
//simple, works, no type args or even type declarations on params I'm just a crazy person who wants it all!
let add = |x, y| x + y;
let add1 = |x| add(x, 1);
So its really just an academic pursuit at this point, it hardly seems practical
I am not sure whether or not I understand your question, but I think maybe this library may help you: https://docs.rs/partial_application/0.2.0/partial_application/
#[macro_use]
extern crate partial_application;
//ltr compose for closures
macro_rules! pipe {
($init:tt |> $($fn:ident),+) => {{
let mut r = $init;
$( r = $fn(r); )*
r
}}
}
fn main() {
let add = |x:i32, y:i32| x + y;
let sq = |x:i32| x * x;
let dbl = |x:i32| x * 2;
//blargh i hate those typeargs! i need them for the matcher
//to get the original params number and type since rust has no reflection :(
//weirdly if i ignore them with _ it works. But it will luckily fail
//hard if you supply incorrect args.
let plus1 = partial!(add => 1,_);
let r = pipe!(3 |> dbl, plus1, sq);
print!("{}", r);
}
You may refer to this library to create your own macro.
Related
I was doing an exercise on exercism with Rust and came across a community solution where the implementation of the given user confused me.
pub struct Position(pub i16, pub i16);
impl Position {
pub fn manhattan(&self) -> i16 {
let Position(x, y) = self;
x.abs() + y.abs()
}
}
What I do not understand is:
let Position(x, y) = self;
x.abs() + y.abs()
As I understand it, I am initializing a struct of Position, but how am I initializing it without setting it equal to a variable? Further more, how am I then able to suddenly have access to the arguments?
What you see here is called destructuring, and is actually creating variables x and y from self.
In this case, if you were to take this line of code:
let Position(x, y) = self;
What this is actually saying is equilalent to this:
let x = &self.0;
let y = &self.1;
You can think of the Position(x, y) part of the assignment as a pattern. You will see pattern matching in many places in Rust, most commonly in match statements. For example, unwrapping a variable of type Option<String> might look like:
match some_optional_string {
Some(value) => println!("Got value: {value}"),
None => println!("Got no value")
};
Pattern matching also appears in conditions as well.
if let Some(value) = some_optional_string {
println!("Got value: {value}");
}
Another way to think of the particular example that you are referring to, is to replace self with its actual value. Say for example self was initialized with the values 3 and 4, this might look like so:
let some_position = Position(3, 4);
Now if you were to call some_position.manhatten(), self is equivalent to a &Position(3, 4)
If you replace all uses of self in the function with the value:
let Position(x, y) = &Position(3, 4);
x.abs() + y.abs()
(This won't actually compile because of lifetimes, but this is beside the point)
You can see more clearly now that Position(x, y) matches &Position(3, 4) but assigning x and y to &3 and &4
I saw you were also learning Haskell, which has a very similar concept. In haskell you could have a function that took in a Maybe value, and have different definitions based on which it matched.
someFunc :: Maybe String -> String
someFunc (Just value) = value
someFunc Nothing = "I'm a different string"
Which has the same concept of destructuring or matching.
Hopefully this rambling makes sense!
I'm working on a rust project written a couple of years ago, and have come across this piece of code, which is literally:
let mut values = vec![];
for x in maybe_values {
if let Some(x) = x {
values.push(Arc::new(x));
}
}
I understand that "if let" introduces a pattern-matching if (Which seems to be a poor re-use of the keyword "let", but I will get over that - If anyone can help me with a mental mnemonic to make sense of "let" here, please do!).
But what is the test Some(x) = x doing?
From my testing, it seems to be a trick/idiom to both a) test that the loop variant 'x' is Some(), and also b) end up with the unwrap()ped value in x.
But I can't fully explain it to myself, and can't find reference to this being an idiom anywhere.
Hope you can help my Rust education path. Thanks.
This is a shorthand for using a full match statement when you only care about matching a single use case.
So this block of code:
if let x = y {
foo();
} else {
bar();
}
Is equivalent to using a full match:
match y {
x => {
foo();
}
_ => {
bar();
}
}
For your specific case, it is equivalent to this. The inner x uses the same name as the outer variable which can be confusing, but they are two separate values.
let mut values = vec![];
for x in maybe_values {
match x {
Some(y) => values.push(Arc::new(y)),
_ => {},
}
}
There are two completely different variables in play here. It's equivalent to.
let mut values = vec![];
for x_1 in maybe_values {
if let Some(x_2) = x_1 {
values.push(Arc::new(x_2));
}
}
In Rust, the right-hand side of a let is evaluated with the left-hand variable not in scope, so when the if let is evaluated, the outer x is still in-scope. Then, if it's a Some value, we make a new variable x which contains the inside of the Option. This variable shadows the previous x, making it inaccessible inside the if statement (in the same way that a function argument called x would render a global variable named x inaccessible by shadowing).
How can I port this C++ code to Rust:
auto sgnR = (R >= 0.) ? 1. : -1.;
I have seen some examples with the match keyword, but I don't understand how it works.
Rust does not have the ternary operator because it's not needed. Everything evaluates to some value, and if / else statements are no exception:
let r = 42.42;
let sgn_r = if r >= 0. { 1. } else { -1. };
You'll note that I've also changed your variable names to be idiomatic Rust. Identifiers use snake_case.
Do not be confused by the ? operator that Rust does have. This is called the "try operator" and is used to propagate errors.
Specifically for this code, it's likely you should use f64::signum:
let r = 42.42_f64;
let sgn_r = r.signum();
You can use bool::then or in this case, the non-lazy bool::then_some to accomplish the same thing:
let sgn_r = (r >= 0).then_some(1).unwrap_or(-1);
An if/else statement is probably better for readability, but this may be nicer in certain cases.
I like to implement a similar construct using a trait:
pub trait IfElse {
fn ifelse<T>(&self, iftrue: T, iffalse: T) -> T;
}
impl IfElse for bool {
fn ifelse<T>(&self, iftrue: T, iffalse: T) -> T {
if *self { iftrue }
else { iffalse }
}
}
Then it can be used like this:
let sng_r = (r >= 0).ifelse(1, -1);
When using below function:
fn factors(number: &BigInt) -> Vec<BigInt> {
let mut n = number.clone();
let mut i: BigInt = ToBigInt::to_bigint(&2).unwrap();
let mut factors = Vec::<BigInt>::new();
while i * i <= n {
if (n % i) == ToBigInt::to_bigint(&1).unwrap() {
i = i + ToBigInt::to_bigint(&1).unwrap();
}
else {
n = n/i as BigInt;
factors.push(i);
}
i = i + ToBigInt::to_bigint(&1).unwrap();
}
if n > i {
factors.push(n);
}
factors
}
I get moved value errors for literally every time i or n is used, starting from the line with while, also in the if. I have read about borrowing, which I understand decently, but this thing I don't understand.
I am not "copying" the value at all, so I don't see anywhere were I could lose ownership of the variables.
Mul (and the other arithmetic operators) take the parameters by value, so i * i move the value i (this is not a problem for primitive numbers because they implement Copy - BigInt does not).
As Mul is implemented for (two) &BigInt, you can do the multiplication (and the other arithmetic operations) with &:
use num::*;
fn factors(number: &BigInt) -> Vec<BigInt> {
let mut n = number.clone();
let mut i = BigInt::from(2);
let mut factors = Vec::new();
while &i * &i <= n {
if (&n % &i) == BigInt::one() {
i = i + BigInt::one();
} else {
n = n / &i;
factors.push(i.clone());
}
i = i + BigInt::one();
}
if n > i {
factors.push(n);
}
factors
}
Note that I also made some simplifications, like omitting the type on Vec::new and using BigInt::from (cannot fail).
Remember that operators in Rust are just syntactic sugar for function calls.
a + b translates to a.add(b).
Primitive types such as i32 implement the trait Copy. Thus, they can be copied into such an add function and do not need to be moved.
I assume the BigInt type you are working with does not implement this trait.
Therefore, in every binary operation you are moving the values.
For example:
enum FooBar {
Bar(Vec<int>),
Other
}
fn main() {
let default:&[int] = [];
let x = Bar(Vec::from_slice([1, 2, 3]));
let y = match(x) {
Bar(z) => z.slice(0, z.len()),
_ => default
};
println!("{}", y);
}
In this example the slice is not valid because of the lifetime on z; but we know that the actual data here (x) is valid for the entire block.
Is there some 'a syntax you can use here to help hint this to compiler? If so, how do you do it?
(I believe I've seen some kind of 'a match { } or match x 'a { } / something syntax in the past, but I can't find any examples now that I'm looking)
Sure, there is some syntax, though it is not as explicit on lifetimes as you mean and in fact it is more general. By doing match as you do you are actually moving x into the match, so it is consumed. No wonder that z.slice(...) can't escape the block. But you can avoid moving x into the match by using reference patterns (note the ref before z):
let default: &[int] = [];
let x = Bar(vec![1, 2, 3]);
let y = match x {
Bar(ref z) => z.slice(0, z.len()),
Other => default
};
println!("{}", y);
This way x is not moved into match block - its internals are borrowed instead. This program compiles and prints out [1, 2, 3].