Return value if passes predicate, else default - rust

How can I replace a value if it fails a predicate?
To illustrate:
assert_eq!((3-5).but_if(|v| v < 0).then(0), 0)
I thought there would be something on Option or Result to allow this, but I cannot find it.

I thought there would be something on Option or Result
But neither of these types appear here. Subtracting two numbers yields another number.
It appears you just want a traditional if-else statement:
fn main() {
let a = 3 - 5;
assert_eq!(if a < 0 { 0 } else { a }, 0);
}
Since you have two values that can be compared, you may also be interested in max:
use std::cmp::max;
fn main() {
assert_eq!(max(0, 3 - 5), 0);
}
You can make your proposed syntax work, but I'm not sure it's worth it. Presented without further comment...
fn main() {
assert_eq!((3 - 5).but_if(|&v| v < 0).then(0), 0)
}
trait ButIf: Sized {
fn but_if<F>(self, f: F) -> ButIfTail<Self>
where F: FnOnce(&Self) -> bool;
}
// or `impl<T> ButIf for T {` for maximum flexibility
impl ButIf for i32 {
fn but_if<F>(self, f: F) -> ButIfTail<Self>
where F: FnOnce(&Self) -> bool,
{
ButIfTail(f(&self), self)
}
}
struct ButIfTail<T>(bool, T);
impl<T> ButIfTail<T> {
fn then(self, alt: T) -> T {
if self.0 {
alt
} else {
self.1
}
}
}

Update: This got a bit nicer since Rust 1.27, when Option::filter was added:
assert_eq!(Some(3 - 5).filter(|&v| v >= 0).unwrap_or(0), 0);
Prior to Rust 1.27, you would have needed an iterator in order to write a single, chained expression without lots of additional custom machinery:
assert_eq!(Some(3 - 5).into_iter().filter(|&v| v >= 0).next().unwrap_or(0), 0);

Related

Recursive closure inside a function [duplicate]

This is a very simple example, but how would I do something similar to:
let fact = |x: u32| {
match x {
0 => 1,
_ => x * fact(x - 1),
}
};
I know that this specific example can be easily done with iteration, but I'm wondering if it's possible to make a recursive function in Rust for more complicated things (such as traversing trees) or if I'm required to use my own stack instead.
There are a few ways to do this.
You can put closures into a struct and pass this struct to the closure. You can even define structs inline in a function:
fn main() {
struct Fact<'s> { f: &'s dyn Fn(&Fact, u32) -> u32 }
let fact = Fact {
f: &|fact, x| if x == 0 {1} else {x * (fact.f)(fact, x - 1)}
};
println!("{}", (fact.f)(&fact, 5));
}
This gets around the problem of having an infinite type (a function that takes itself as an argument) and the problem that fact isn't yet defined inside the closure itself when one writes let fact = |x| {...} and so one can't refer to it there.
Another option is to just write a recursive function as a fn item, which can also be defined inline in a function:
fn main() {
fn fact(x: u32) -> u32 { if x == 0 {1} else {x * fact(x - 1)} }
println!("{}", fact(5));
}
This works fine if you don't need to capture anything from the environment.
One more option is to use the fn item solution but explicitly pass the args/environment you want.
fn main() {
struct FactEnv { base_case: u32 }
fn fact(env: &FactEnv, x: u32) -> u32 {
if x == 0 {env.base_case} else {x * fact(env, x - 1)}
}
let env = FactEnv { base_case: 1 };
println!("{}", fact(&env, 5));
}
All of these work with Rust 1.17 and have probably worked since version 0.6. The fn's defined inside fns are no different to those defined at the top level, except they are only accessible within the fn they are defined inside.
As of Rust 1.62 (July 2022), there's still no direct way to recurse in a closure. As the other answers have pointed out, you need at least a bit of indirection, like passing the closure to itself as an argument, or moving it into a cell after creating it. These things can work, but in my opinion they're kind of gross, and they're definitely hard for Rust beginners to follow. If you want to use recursion but you have to have a closure, for example because you need something that implements FnOnce() to use with thread::spawn, then I think the cleanest approach is to use a regular fn function for the recursive part and to wrap it in a non-recursive closure that captures the environment. Here's an example:
let x = 5;
let fact = || {
fn helper(arg: u64) -> u64 {
match arg {
0 => 1,
_ => arg * helper(arg - 1),
}
}
helper(x)
};
assert_eq!(120, fact());
Here's a really ugly and verbose solution I came up with:
use std::{
cell::RefCell,
rc::{Rc, Weak},
};
fn main() {
let weak_holder: Rc<RefCell<Weak<dyn Fn(u32) -> u32>>> =
Rc::new(RefCell::new(Weak::<fn(u32) -> u32>::new()));
let weak_holder2 = weak_holder.clone();
let fact: Rc<dyn Fn(u32) -> u32> = Rc::new(move |x| {
let fact = weak_holder2.borrow().upgrade().unwrap();
if x == 0 {
1
} else {
x * fact(x - 1)
}
});
weak_holder.replace(Rc::downgrade(&fact));
println!("{}", fact(5)); // prints "120"
println!("{}", fact(6)); // prints "720"
}
The advantages of this are that you call the function with the expected signature (no extra arguments needed), it's a closure that can capture variables (by move), it doesn't require defining any new structs, and the closure can be returned from the function or otherwise stored in a place that outlives the scope where it was created (as an Rc<Fn...>) and it still works.
Closure is just a struct with additional contexts. Therefore, you can do this to achieve recursion (suppose you want to do factorial with recursive mutable sum):
#[derive(Default)]
struct Fact {
ans: i32,
}
impl Fact {
fn call(&mut self, n: i32) -> i32 {
if n == 0 {
self.ans = 1;
return 1;
}
self.call(n - 1);
self.ans *= n;
self.ans
}
}
To use this struct, just:
let mut fact = Fact::default();
let ans = fact.call(5);

Integer operation with boundary when overflow in Rust

The problem I recently meet requires to do integer operation with boundary based on the bits of integer type.
For example, using i32 integer to do add operation, here's a piece of pseudo code to present the idea:
sum = a + b
max(min(sum, 2147483647), -2147483648)
// if the sum is larger than 2147483647, then return 2147483647.
// if the sum is smaller than -2147483648, then return -2147483648.
To achieve this, I naively wrote following ugly code:
fn i32_add_handling_by_casting(a: i32, b: i32) -> i32 {
let sum: i32;
if (a as i64 + b as i64) > 2147483647 as i64 {
sum = 2147483647;
} else if (a as i64 + b as i64) < -2147483648 as i64 {
sum = -2147483648;
} else {
sum = a + b;
}
sum
}
fn main() {
println!("{:?}", i32_add_handling_by_casting(2147483647, 1));
println!("{:?}", i32_add_handling_by_casting(-2147483648, -1));
}
The code works well; but my six sense told me that using type casting is problematic. Thus, I tried to use traditional panic (exception) handling to deal with this...but I stuck with below code (the panic result can't detect underflow or overflow):
use std::panic;
fn i32_add_handling_by_panic(a: i32, b: i32) -> i32 {
let sum: i32;
let result = panic::catch_unwind(|| {a + b}).ok();
match result {
Some(result) => { sum = result },
None => { sum = ? }
}
sum
}
fn main() {
println!("{:?}", i32_add_handling_by_panic(2147483647, 1));
println!("{:?}", i32_add_handling_by_panic(-2147483648, -1));
}
To sum up, I have 3 questions:
Is my type casting solution valid for strong typing language? (If possible, I need the explanation why it's valid or not valid.)
Is there other better way to deal with this problem?
Could panic handle different exception separately?
In this case, the Rust standard library has a method called saturating_add, which supports your use case:
assert_eq!(10_i32.saturating_add(20), 30);
assert_eq!(i32::MIN.saturating_add(-1), i32::MIN);
assert_eq!(i32::MAX.saturating_add(1), i32::MAX);
Internally, it is implemented as a compiler intrinsic.
In general, such problems are not intended to be solved with panics and unwinding, which are intended for cleanup in exceptional cases only. A hand-written version might involve type casting, but calculating a as i64 + b as i64 only once. Alternatively, here's a version using checked_add, which returns None rather than panics in case of overflow:
fn saturating_add(a: i32, b: i32) -> i32 {
if let Some(sum) = a.checked_add(b) {
sum
} else if a < 0 {
i32::MIN
} else {
i32::MAX
}
}

How to avoid cloning a big integer in rust

I used the num::BigUInt type to avoid integer overflows when calculating the factorial of a number.
However, I had to resort to using .clone() to pass rustc's borrow checker.
How can I refactor the factorial function to avoid cloning what could be large numbers many times?
use num::{BigUint, FromPrimitive, One};
fn main() {
for n in -2..33 {
let bign: Option<BigUint> = FromPrimitive::from_isize(n);
match bign {
Some(n) => println!("{}! = {}", n, factorial(n.clone())),
None => println!("Number must be non-negative: {}", n),
}
}
}
fn factorial(number: BigUint) -> BigUint {
if number < FromPrimitive::from_usize(2).unwrap() {
number
} else {
number.clone() * factorial(number - BigUint::one())
}
}
I tried to use a reference to BigUInt in the function definition but got some errors saying that BigUInt did not support references.
The first clone is easy to remove. You are trying to use n twice in the same expression, so don't use just one expression:
print!("{}! = ", n);
println!("{}", factorial(n));
is equivalent to println!("{}! = {}", n, factorial(n.clone())) but does not try to move n and use a reference to it at the same time.
The second clone can be removed by changing factorial not to be recursive:
fn factorial(mut number: BigUint) -> BigUint {
let mut result = BigUint::one();
let one = BigUint::one();
while number > one {
result *= &number;
number -= &one;
}
result
}
This might seem unidiomatic however. There is a range function, that you could use with for, however, it uses clone internally, defeating the point.
I don't think take a BigUint as parameter make sense for a factorial. u32 should be enough:
use num::{BigUint, One};
fn main() {
for n in 0..42 {
println!("{}! = {}", n, factorial(n));
}
}
fn factorial_aux(accu: BigUint, i: u32) -> BigUint {
if i > 1 {
factorial_aux(accu * i, i - 1)
}
else {
accu
}
}
fn factorial(n: u32) -> BigUint {
factorial_aux(BigUint::one(), n)
}
Or if you really want to keep BigUint:
use num::{BigUint, FromPrimitive, One, Zero};
fn main() {
for i in (0..42).flat_map(|i| FromPrimitive::from_i32(i)) {
print!("{}! = ", i);
println!("{}", factorial(i));
}
}
fn factorial_aux(accu: BigUint, i: BigUint) -> BigUint {
if !i.is_one() {
factorial_aux(accu * &i, i - 1u32)
} else {
accu
}
}
fn factorial(n: BigUint) -> BigUint {
if !n.is_zero() {
factorial_aux(BigUint::one(), n)
} else {
BigUint::one()
}
}
Both version doesn't do any clone.
If you use ibig::UBig instead of BigUint, those clones will be free, because ibig is optimized not to allocate memory from the heap for numbers this small.

How to express integers other than zero and one in generic code using the num crate?

The num crate in Rust provides a way of representing zeros and ones via T::zero() and T::one(). Is there a way of representing other integers, such as two, three, etc.?
Consider the following (artificial) example:
extern crate num;
trait IsTwo {
fn is_two(self) -> bool;
}
impl<T: num::Integer> IsTwo for T {
fn is_two(self) -> bool {
self == (T::one() + T::one())
}
}
Is there a better way of representing T::one() + T::one() as 2?
One way of representing arbitrary integers in generic code is to use the num::NumCast trait:
impl<T: num::Integer + num::NumCast> IsTwo for T {
fn is_two(self) -> bool {
self == T::from(2).unwrap()
}
}
A related way is to use the num::FromPrimitive trait:
impl<T: num::Integer + num::FromPrimitive> IsTwo for T {
fn is_two(self) -> bool {
self == T::from_i32(2).unwrap()
}
}
Related questions and answers: [1, 2].
You can write a function:
fn two<T>() -> T
where T: num::Integer,
{
let mut v = T::zero();
for _ in 0..2 {
v = v + T::one();
}
v
}
I've chosen this form because it's easily made into a macro, which can be reused for any set of values:
num_constant!(two, 2);
num_constant!(forty_two, 42);
I hear the concerns now... "but that's a loop and inefficient!". That's what optimizing compilers are for. Here's the LLVM IR for two when compiled in release mode:
; Function Attrs: noinline readnone uwtable
define internal fastcc i32 #_ZN10playground3two17hbef99995c3606e93E() unnamed_addr #3 personality i32 (i32, i32, i64, %"unwind::libunwind::_Unwind_Exception"*, %"unwind::libunwind::_Unwind_Context"*)* #rust_eh_personality {
bb3:
br label %bb8
bb8: ; preds = %bb3
ret i32 2
}
That's right - it's been optimized to the value 2. No loops.
It's relatively simple to forge any number from 0 and 1:
you need to create 2, which is hardly difficult
you then proceed in converting your number to base 2, which takes O(log2(N)) operations
The algorithm is dead simple:
fn convert<T: Integer>(n: usize) -> T {
let two = T::one() + T::one();
let mut n = n;
let mut acc = T::one();
let mut result = T::zero();
while n > 0 {
if n % 2 != 0 {
result += acc;
}
acc *= two;
n /= 2;
}
result
}
And will be efficient both in Debug (O(log2(N)) iterations) and Release (the compiler optimizes it out completely).
For those who wish to see it in action, here on the playground we can see that convert::<i32>(12345) is optimized to 12345 as expected.
As an exercise to the reader, implement a generic version of convert which takes any Integer parameter, there's not much operations required on n after all.

Creating an iterator that either steps upwards or downwards

I'd ideally like to have something like the following:
iter = if go_up {
(min .. limit)
} else {
(limit .. max).rev()
};
to create an iterator that either counts up or down to some limit, depending on the situation. However, because Range and Rev are different types, I can't do this. I can use the step_by feature, but because my limits are an unsigned data-type, I then also have to cast everything. The best I have so far is:
#![feature(step_by)]
iter = if go_up {
(min as i64 .. limit as i64).step_by(1)
} else {
(limit as i64 .. max as i64).step_by(-1)
};
but this requires both unstable features, and shoehorning my types. It seems like there should be a neater way to do this; does anyone know one?
The direct solution is to simply create an iterator that can either count upwards or downwards. Use an enum to choose between the types:
use std::ops::Range;
use std::iter::Rev;
enum Foo {
Upwards(Range<u8>),
Downwards(Rev<Range<u8>>),
}
impl Foo {
fn new(min: u8, limit: u8, max: u8, go_up: bool) -> Foo {
if go_up {
Foo::Upwards(min..limit)
} else {
Foo::Downwards((limit..max).rev())
}
}
}
impl Iterator for Foo {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
match *self {
Foo::Upwards(ref mut i) => i.next(),
Foo::Downwards(ref mut i) => i.next(),
}
}
}
fn main() {
for i in Foo::new(1, 5, 10, true) {
println!("{}", i);
}
for i in Foo::new(1, 5, 10, false) {
println!("{}", i);
}
}
Another pragmatic solution that introduces a little bit of indirection is to Box the iterators:
fn thing(min: u8, limit: u8, max: u8, go_up: bool) -> Box<Iterator<Item = u8>> {
if go_up {
Box::new(min..limit)
} else {
Box::new((limit..max).rev())
}
}
fn main() {
for i in thing(1, 5, 10, true) {
println!("{}", i);
}
for i in thing(1, 5, 10, false) {
println!("{}", i);
}
}
Personally, your solution
iter = if go_up {
(min as i64 .. limit as i64).step_by(1)
} else {
(limit as i64 .. max as i64).step_by(-1)
};
is a better option than Shepmaster's first example, since it's more complete (eg. there's a size_hint), it's more likely to be correct by virtue of being a standard tool and it's faster to write.
It's true that this is unstable, but there's nothing stopping you from just copying the source in the meantime. That gives you a nice upgrade path for when this eventually gets stabilized.
The enum wrapper technique is great in more complex cases, though, but in this case I'd be tempted to KISS.

Resources