Is there a way to make rust support this syntax: ?continue

loop {
        // if error, continue.
        let s = std::fs::read_to_string("1.txt")?continue;
        // ...
}

The match {} syntax is not elegant enough, and it's more verbose.

Can it be implemented with macros ? (or other way)

The guard crate has a macro that looks like this:

guard!(let Ok(s) = std::fs::read_to_string("1.txt") else { continue });

You could also make a custom macro for your use case, like this:

macro_rules! unwrap_or_continue {
    ($e:expr) => {
        match $e {
            Ok(x) => x,
            Err(_) => continue,
        }
    }
}

which would be used like this:

let s = unwrap_or_continue!(std::fs::read_to_string("1.txt"));
2 Likes

thanks, It works, but I think it is not elegant enough.

You can write a macro that intercepts ? and retries a block of code until it succeeds:

struct Ignore;
// Debug bound to avoid conflict w/ reflexive impl
impl <T: core::fmt::Debug> From<T> for Ignore {
    fn from(_: T) -> Self {
        Ignore
    }
}

macro_rules! retry_loop {
    { $($tt:tt)* } => {
        loop {
            match ( ||->Result<_, Ignore> {
                Ok({$($tt)*})
            })() {
                Err(_) => { continue; },
                Ok(out) => { break out; }
            };
        };
    };
}

fn main() {
    let mut x = 0;

    let y = retry_loop! {
        x += 1;
        if x < 3 { Err(())? }
        "Hello"
    };

    dbg!((x, y));  // (3, "Hello")
}

(Playground)

5 Likes

It works, but closures may have efficiency, life cycle, and static type derivation problems in rust. I am still afraid of it. I hope the official solution will give an elegant syntactic sugar.

Can you elaborate on what you mean by this?

Rust doesn’t have any kind of a postfix conditional construct (other than ?, which requires a function boundary), so the ultimate solution will have to put something at the start of the expression that’s being guarded. If that location isn’t explicitly marked, there needs to be a parser with arbitrary lookahead involved to figure out where the prefix part should go. That’s a complicated bit of software engineering, regardless of the mechanism involved, whether it’s macro_rules or a procedural macro.

1 Like

about closure:

let a = "hello";
let b = move || {
     let c = a + 1; // a move to here.
}
println!("{}", a); // error: a moved! 

I have to move it out from closure. but if I have a lot of variables... reference have other inconvenient. so I think it is better not to turn the statement into a closure, which may bring some additional potential problems.

May be need to add plug-ins to the rust parser, I hope the official can consider it, after all, this is very common. I also saw that many people in the community raised this issue.

try blocks are a thing on nightly.

Playground (adapted from @2e71828's post)

2 Likes

nice! but can't continue when Error or None return.

What would you consider elegant enough? Like it or not, the language does not currently have the exact syntax you're looking for; I'm not even aware of any common language that does.

You asked if it could be implemented using macros. The answer is yes, and multiple examples were provided. If you're looking for something else, you should lay down your requirements clearly rather than just rejecting what someone provided.

2 Likes

I am just looking for a more elegant solution, not limited to macros. Thank you for your proposal.

First off, in my experience, it's not at all that common. Idiomatic Rust code tends to favor iterators and simple loops — if you are consistently trying to fight the language with something very convoluted, then you should consider refactoring your code so that it flows more naturally, rather than trying to bend the language to your own will and style.

Second, introducing globality, context-dependence, and generally similar, vastly complex constructs is not a great way to approach one-off problems. Due to Rust's stability guarantees, once a change enters the language, it's basically stuck there forever. Thus, even minor changes need to have a very strong motivation in order to hit the threshold for inclusion. "I have to type two more lines" is basically never strong enough motivation unto itself in this regard.

4 Likes

What do you mean by more elegant, though? That's what I'm asking, especially because you explicitly mention macros (and nothing else) in your post.

I just tested the experimental feature "try {} " in nightly version, which can solve my problem to some extent, but not perfect.

The problem that you said that the code structure is complex and needs to be refactored does exist, but it occurs naturally with the business. After splitting into functions, the problem does not exist, but I think this "experimental feature try {} " is still very useful, not something They were all thoughts at the beginning.

What about x?'foo continuing the loop labeled 'foo:

'a: for item in stuff {
    println!("{}", item?'a);
}

Would this syntax create problems or inconsistencies?

Something that seems inconsistent is that there is a way to continue, but not to break. (or x?!'foo could be the break syntax?)

Previously discussed on the internals list, which itself links to longer discussions.

2 Likes

? is a very good syntactic sugar.

expect this proposal, don’t know when it will be experimented in the nightly version