Function composition chain with a pure macro in Rust - rust

I've read
How to compose functions in Rust?
Function composition chain in Rust
I've learned implementing a Function composition chain in Rust is rather difficult, and people use Macro with some function, however, I wonder if it's possible to use only a macro without a compose function from the first place.
I mean
compose!(f, g) can be simply reritten to |x| g(f(x)) (just another syntax)
or
compose!(f, g, h) can be similarly rewitten to |x| h(g(f(x)))
compose!(f, g, h, i) can be similarly rewitten to |x| i(h(g(f(x))))
compose!(f, g, h, i,...) can be similarly rewitten to |x| ...(i(h(g(f(x)))))
as the recursive manner.
I guess this recursive macro does not need the actual function composition function.
I've just started learning Rust macro, so what would be the smart way to code this?
PS. Does the type-inference work fine with macro in Rust?

Yes, you can do that only using macros:
macro_rules! compose {
($($rest:ident),+) => {
|x| { compose!(expand x, $($rest),*) }
};
(expand $inner:expr, $function:ident, $($rest:ident),*) => {
compose!(expand $function($inner), $($rest),*)
};
(expand $inner:expr, $function:ident) => {
$function($inner)
};
}
let a = compose!(f, g, h, i);
//expands into:
let a = |x| i(h(g(f(x))));

Related

Procedural Macros for custom operator of T in Rust

Basically, I want to implement custom operator in Rust.
One is pipeline-operator that is on T.
operator overloading only works for local structures so not usable for type T.
On the other hand, apply::Apply does work on T
/// Represents a type which can have functions applied to it (implemented
/// by default for all types).
pub trait Apply<Res> {
/// Apply a function which takes the parameter by value.
fn apply<F: FnOnce(Self) -> Res>(self, f: F) -> Res
where Self: Sized {
f(self)
}
}
impl<T: ?Sized, Res> Apply<Res> for T {
// use default definitions...
}
let string = 1.apply(|x| x * 2).apply(|x: i32| x.to_string());
However, I don't like this .apply syntax. It's the same as Prototype Pollution in JavaScript and can collide with some-structure.apply
In any case, I prefer binary operator like
let string = 1 |> (|x| x * 2) |> (|x: i32| x.to_string());
So my question is:
Writing the code above with a binary operator such as |>, is it possible to transpile the code (Macro input) to something (Macro output) like
let string = 1.myownapply(|x| x * 2).myownapply(|x: i32| x.to_string());
myownapply can be a much longer strings to avoid the collision.
I've read Procedural Macros in Rust can do anything, so is this possible?
If this is not so hard, I appreciate if you provide a sample code.
This should be possible with macro_rules!, see https://doc.rust-lang.org/rust-by-example/macros.html and https://doc.rust-lang.org/reference/macros-by-example.html. Try something like this:
macro_rules! pipeline {
($expr:expr $(=> $func:expr)+) => {
$expr $(.apply($func))+
}
}

Flatten vector of enums in Rust

I am trying to flatten a vector of Enum in Rust, but I am having some issues:
enum Foo {
A(i32),
B(i32, i32),
}
fn main() {
let vf = vec![Foo::A(1), Foo::A(2), Foo::B(3, 4)];
let vi: Vec<i32> = vf
.iter()
.map(|f| match f {
Foo::A(i) => [i].into_iter(),
Foo::B(i, j) => [i, j].into_iter(),
})
.collect(); // this does not compile
// I want vi = [1, 2, 3, 4]. vf must still be valid
}
I could just use a regular for loop and insert elements into an existing vector, but that would be no fun. I'd like to know if there is a more idiomatic Rust way of doing it.
Here's a way to do it that produces an iterator (rather than necessarily a vector, as the fold() based solution does).
use std::iter::once;
enum Foo {
A(i32),
B(i32, i32),
}
fn main() {
let vf = vec![Foo::A(1), Foo::A(2), Foo::B(3, 4)];
let vi: Vec<i32> = vf
.iter()
.flat_map(|f| {
match f {
&Foo::A(i) => once(i).chain(None),
&Foo::B(i, j) => once(i).chain(Some(j)),
}
})
.collect();
dbg!(vi);
}
This does essentially the same thing that you were attempting, but in a way which will succeed. Here are the parts I changed, in the order they appear in the code:
I used .flat_map() instead of .map(). flat_map accepts a function which returns an iterator and produces the elements of that iterator ("flattening") whereas .map() would have just given the iterator.
I used & in the match patterns. This is because, since you are using .iter() on the vector (which is appropriate for your requirement “vf must still be valid”), you have references to enums, and pattern matching on a reference to an enum will normally give you references to its elements, but we almost certainly want to handle the i32s by value instead. There are several other things I could have done, such as using the * dereference operator on the values instead, but this is concise and tidy.
You tried to .into_iter() an array. Unfortunately, in current Rust this does not do what you want and you can't actually return that iterator, for somewhat awkward reasons (which will be fixed in an upcoming Rust version). And then, if it did mean what you wanted, then you'd get an error because the two match arms have unequal types — one is an iterator over [i32; 1] and the other is an iterator over [i32; 2].
Instead, you need to build two possible iterators which are clearly of the same type. There are lots of ways to do this, and the way I picked was to use Iterator::chain to combine once(i), an iterator that returns the single element i, with an Option<i32> (which implements IntoIterator) that contains the second element j if it exists.
Notice that in the first match arm I wrote the seemingly useless expression .chain(None); this is so that the two arms have the same type. Another way to write the same thing, which is arguably clearer since it doesn't duplicate code that has to be identical, is:
let (i, opt_j) = match f {
&Foo::A(i) => (i, None),
&Foo::B(i, j) => (i, Some(j)),
};
once(i).chain(opt_j)
In either case, the iterator's type is std::iter::Chain<std::iter::Once<i32>, std::option::IntoIter<i32>> — you don't need to know this exactly, just notice that there must be a type which handles both the A(i) and the B(i, j) cases.
First of all, you need to change the i32 references to owned values by e.g. dereferencing them. Then you can circumvent proxying through inlined arrays by using fold():
enum Foo {
A(i32),
B(i32, i32),
}
fn main() {
let vf = vec![Foo::A(1), Foo::A(2), Foo::B(3, 4)];
let vi: Vec<i32> = vf
.iter()
.fold(Vec::new(), |mut acc, f| {
match f {
Foo::A(i) => acc.push(*i),
Foo::B(i, j) => {
acc.push(*i);
acc.push(*j);
}
}
acc
});
}

How can I (slice) pattern match on an owned Vec with non-Copy elements?

My goal is to move elements out of an owned Vec.
fn f<F>(x: Vec<F>) -> F {
match x.as_slice() {
&[a, b] => a,
_ => panic!(),
}
}
If F is copy, that is no problem as one can simply copy out of the slice. When F is not, slice patterns seem a no-go, as the slice is read only.
Is there such a thing as an "owned slice", or pattern matching on a Vec, to move elements out of x?
Edit: I now see that this code has the more general problem. The function
fn f<T>(x: Vec<T>) -> T {
x[0]
}
leaves "a hole in a Vec", even though it is dropped right after. This is not allowed. This post and this discussion describe that problem.
That leads to the updated question: How can a Vec<T> be properly consumed to do pattern matching?
If you insist on pattern matching, you could do this:
fn f<F>(x: Vec<F>) -> F {
let mut it = x.into_iter();
match (it.next(), it.next(), it.next()) {
(Some(x0), Some(_x1), None) => x0,
_ => panic!(),
}
}
However, if you just want to retrieve the first element of a 2-element vector (panicking in other cases), I guess I'd rather go with this:
fn f<F>(x: Vec<F>) -> F {
assert_eq!(x.len(), 2);
x.into_iter().next().unwrap()
}
You can't use pattern matching with slice patterns in this scenario.
As you have correctly mentioned in your question edits, moving a value out of a Vec leaves it with uninitialized memory. This could then cause Undefined Behaviour when the Vec is subsequently dropped, because its Drop implementation needs to free the heap memory, and possibly drop each element.
There is currently no way to express that your type parameter F does not have a Drop implementation or that it is safe for it to be coerced from uninitialized memory.
You pretty much have to forget the idea of using a slice pattern and write it more explicitly:
fn f<F>(mut x: Vec<F>) -> F {
x.drain(..).next().unwrap()
}
If you are dead set on pattern matching, you can use Itertools::tuples() to match on tuples instead:
use itertools::Itertools; // 0.9.0
fn f<F>(mut x: Vec<F>) -> F {
match x.drain(..).tuples().next() {
Some((a, _)) => a,
None => panic!()
}
}
One way to achieve consuming a single element of a vector is to swap the last element with the element you want to consume, and then pop the last element
fn f<F>(mut x: Vec<F>) -> F {
match x.as_slice() {
[_a, _b] => {
x.swap(0, 1);
x.pop().unwrap() // returns a
},
_ => panic!(),
}
}
The code uses an unwrap which isn't elegant.

Can/does rust have a C question mark colon equivalent [duplicate]

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);

Does if-let with a pair short-circuit?

Suppose f() and g() return Option<T>.
if let (Some(x), Some(y)) = (f(), g()) {
h();
}
If f() returns None, will g() be evaluated? Is evaluation guaranteed or prohibited by the spec?
It does not shortcut. To pattern-match against the pair, the pair must be fully constructed, which means both f and g have been called. There is no lazy evaluation where the pattern match could happen before the values are calculated.
(By the way, it's easy to try out, and the Rust compiler pretty much is the spec right now.)

Resources