Conditional in a call chain? Is there a way?

Here's a cute little problem. Pipelines work fine in rust.

let t1 = zero.add(1).mult(2).sub(1);

But what if I need a conditional in the middle of the pipeline?

let t2 = zero.add(2).if t1.v > 1 { mult(1) } else { sub(1) };

Not allowed, of course. It can of course be broken down into multiple statements, but that would be so un-functional. Is there an ideomatic way to do that?

Not like your first example in the playground:

let t2 = zero.add(2).if t1.v > 1 { mult(1) } else { sub(1) };

But it is possible to get it working using your second example:

let cond = |
    value: Foo,
    predicate: fn(_) -> _,
    (true_fn, true_arg): (fn(&Foo, u32) -> Foo, u32),
    (false_fn, false_arg): (fn(&Foo, u32) -> Foo, u32),
| if predicate(value.v) { true_fn(&value, true_arg) } else { false_fn(&value, false_arg) };
let t2 = cond(zero.add(2), |z| z > 1, (Foo::mult, 1), (Foo::sub, 1));

However, as you can imagine, it's incredibly ugly and not idiomatic.

Just stick to an if/else in your code, Rust is not a purely functional language, nor a purely imperative one.

1 Like

Thanks. That's what I expected. I was hoping there was some functional feature I didn't know about.

You could introduce map.

(e) One can also introduce additional types and pipe logic through them like this. I think this is going to far :smiley:

1 Like

FWIW, @KillTheMule's example is bundled within the following very handy crate:

Demo (with Pipe trait in scope):

let t2 =
        .pipe(|it| if t1.v > 1 { it.mult(1) } else { it.sub(1) })

let t2 = zero.add(2).map(|e| if t1.v>1{e.mult(10)} else {e.sub(1)});

Now that's a surprise. I thought map was only for iterators. Is map a trait of everything by default? Or is it just defined for the built-in types?

Look at line 20, I implemented it for your type.