Conditional changes function in a confusing way

While trying to add a feature, I ran into a very strange behavior and I cannot make any sense of it. With all the Rust experts here, I hope you can shed some llight on this.

Here a short overview:

pub fn check(
    host: &str,
    headers: &HeaderMap,
    token: Option<String>,
    tokens: Option<Vec<String>>,
) -> Result<(), Error> {
    // Statements for showing weird behavior
    println!("token.is_some()= {:?}", token.is_some());
    println!("tokens.is_some()= {:?}", tokens.is_some());

    // As soon as I add the following if statement, no code is executed.
    // Not even the 2 println statements above. It only returns Ok(())
    // Run test to see that none of the statements show up.
    // But I have to guard this code, otherwise it will panic (which makes sense)
    // Comment out line 24 and line 54 and run
    // cargo test test_check_auth --

    if token.is_some() || tokens.is_some() {

The full code can be found here rustypaste/src/auth.rs at just-weird · tessus/rustypaste · GitHub

First I though it was a bug in Rust and tried to come up with a minimal code example to reproduce the issue. To my astonishment, almost the same code works on play: Rust Playground

The functions are basically the same, except for 2 additional arguments and the ErrorType being different. Both of which should have nothing to do with this strange behavior.

Commenting the if statement and the matching bracket shows that the println statement are otherwise executed and that token.is_some() and tokens.is_some() are evaluated correctly.

So what am I missing? I have been trying many hours to understand what is going on, but it just doesn't make any sense to me.
How is it possible that an if statement changes the flow that even statements BEFORE the if block are not executed? I am at a loss.

P.S.: I am rather new to Rust so please ignore any non-Rust ways of doing things. However, from a logical perspective the code is sound and any style improvements are secondary.

I think you may be experiencing output-capture during tests. For example:

$ cargo test --lib test_check_auth
    Finished test [unoptimized + debuginfo] target(s) in 0.10s
     Running unittests src/lib.rs (target/debug/deps/rustypaste-d27f38c66d457330)

running 1 test
test auth::tests::test_check_auth ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 27 filtered out; finished in 0.00s
$ cargo test --lib test_check_auth -- --nocapture
    Finished test [unoptimized + debuginfo] target(s) in 0.10s
     Running unittests src/lib.rs (target/debug/deps/rustypaste-d27f38c66d457330)

running 1 test
token.is_some()= true
tokens.is_some()= false
auth_header= Some("test_token")
token.is_some()= true
tokens.is_some()= false
auth_header= Some("test_token")
token.is_some()= false
tokens.is_some()= true
auth_header= Some("test_token")
token.is_some()= false
tokens.is_some()= true
auth_header= Some("test_token")
token.is_some()= false
tokens.is_some()= false
token.is_some()= false
tokens.is_some()= false
token.is_some()= true
tokens.is_some()= false
auth_header= None
test auth::tests::test_check_auth ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 27 filtered out; finished in 0.00s

See test output options for more.

2 Likes

Near as I can tell, this is all expected? Your check code roughly simplifies to:

pub fn check(a: bool, b: bool): Result<(), &'static str> {
   if a || b {
     let mut found = false;
     if a {
        found = true;
     }
     if b {
       found = true;
     }
     if !found {
       return Err("Not found");
     }
   }
   return Ok(());
}

If you remove the outer if, then check(false, false) will now hit the if !found where it wouldn't before.

Ha, so my code is actually working. Hmm, what about that. But why is it not capturing when I remove the if statement? Maybe because it runs into an error then.

Perfect, I will read up on the link you sent.

You have no idea how close I was to losing my sanity. :wink:

Nope, I think you misunderstood the problem. Anyway, all good. Problem wa already solved by @cuviper.

But thanks for looking into this nonetheless.

Right, when a test fails, you always get to see its output.

2 Likes

Just to be clear, I saw the earlier reply, but I was just confirming that the test behavior is what you expected, since it was changing the behavior of the test with and without the if statement.

1 Like

At that point (the time of my reply to you) I did not yet understand that the tests capture any output when there's no error.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.