Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an error return method to format #3206

Open
cheeroncode opened this issue Dec 12, 2021 · 14 comments
Open

Add an error return method to format #3206

cheeroncode opened this issue Dec 12, 2021 · 14 comments

Comments

@cheeroncode
Copy link

cheeroncode commented Dec 12, 2021

Add an error return method to format!
The format! macro never fails and the compiler reports an error when it does.

let a:String = format!("{}",9);

Sometimes, however, I may need to reorganize the formatted string in a custom program macro based on user input.
When the user typed something wrong, I wanted to catch the compiler error and re-report the error in the right place.
I wish there was a way to return the wrong or a way to do this, thanks a lot.

let b:Result<String,String> = try_format!("{:?}",9);
match b {
    Ok(ok) => {
        // do something...
    },
    Err(err) => {
        // Custom error handling...
    },
} 

Here's a scenario that could go wrong to make my intentions more obvious.

I provide my users with a macro called comment!, which takes a format argument like format! :

#[comment("{:1$}","hi~",10)]
fn do(){
    //....
}

comment! inserts a new line of code into method do :

fn do(){
    println!("{:1$}","hi~",10);
    //....
}

If the user accidentally enters formatting parameters incorrectly:

#[comment("{:$}","hi~",10)]
           ^^^^
fn do(){
    //....
}

Then, the newly generated code will be wrong.
The compiler will give a vague error on #[comment(...)] and cannot indicate the correct location:

fn do(){
    println!("{:$}","hi~",10); // ^^^^ section is what actually went wrong, missing the width index.
              ^^^^             
    //....
}          

Now, I want to check that the formatting parameters are correct when I generate 'println'.
I think the best way is to catch any errors the compiler gives you,
And display the error in the corresponding position of comment! :

#[comment("{:$}","hi~",10)]
           ~~~~
fn do(){
    //....
}

So, I wish there was a try_format!.
When the parameter is not correct, can return an error, let me control.

@shepmaster
Copy link
Member

This sounds like something that could be added to calm_io before being added to the standard library.

@cheeroncode
Copy link
Author

@shepmaster Calm_io can accomplish its goals with functionality already provided in the library.
I'd love to see a way to do that and add try_format! to it.
But for now, there is no way to do something like try_format! from the standard library, and everything associated with it is private.

@shepmaster
Copy link
Member

Actually, it's not clear what you mean by failing. Formatting to a string cannot fail at all beyond memory allocation. You also say "catch the compiler error", but then you show runtime code like match. Your example also doesn't fail. It sounds like you need to make a clearer example of the problem and proposed solution(s).

@cheeroncode
Copy link
Author

cheeroncode commented Dec 12, 2021

@shepmaster You're right, I'm just stating my intent, there's nothing wrong with the sample code.
I've added some scenario code to make it more explicit.

@cheeroncode
Copy link
Author

☹️☹️

@kennytm
Copy link
Member

kennytm commented Dec 15, 2021

Formatting to a string cannot fail at all beyond memory allocation.

technically it can panic if the fmt impl returned an error

use std::fmt;

struct Y;

impl fmt::Debug for Y {
    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
        Err(fmt::Error)
    }
}

fn main() {
    dbg!(format!("{:?}", Y)); 
    // panic: "a formatting trait implementation returned an error: Error"
}

but for built-in types like str and integers they won't fail.

@Diggsey
Copy link
Contributor

Diggsey commented Dec 15, 2021

@cheeroncode I'm afraid your suggestion doesn't really make sense.

For your specific example, as long as your #[comment] attribute macro generates code with the correct span information, then the compiler should be able to report the error in the correct place.

The reason your suggestion of try_format! doesn't make sense is because the kinds of errors you are trying to catch from format!, are generated at compile-time. The logic for parsing the curly brackets from the format string doesn't even exist at runtime. Furthermore, this kind of error should not be reported at runtime, because it's known at compile-time whether the format string is valid or not.

If you are unable to generate the correct spans, then you should validate the format string yourself within your procedural macro, and report the error directly using compile_error!.

@cheeroncode
Copy link
Author

Formatting to a string cannot fail at all beyond memory allocation.

technically it can panic if the fmt impl returned an error

use std::fmt;

struct Y;

impl fmt::Debug for Y {
    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
        Err(fmt::Error)
    }
}

fn main() {
    dbg!(format!("{:?}", Y)); 
    // panic: "a formatting trait implementation returned an error: Error"
}

but for built-in types like str and integers they won't fail.

Thanks, but what I really want is an error message returned by the compiler when formatting placeholders fail.

@cheeroncode
Copy link
Author

@cheeroncode I'm afraid your suggestion doesn't really make sense.

For your specific example, as long as your #[comment] attribute macro generates code with the correct span information, then the compiler should be able to report the error in the correct place.

The reason your suggestion of try_format! doesn't make sense is because the kinds of errors you are trying to catch from format!, are generated at compile-time. The logic for parsing the curly brackets from the format string doesn't even exist at runtime. Furthermore, this kind of error should not be reported at runtime, because it's known at compile-time whether the format string is valid or not.

If you are unable to generate the correct spans, then you should validate the format string yourself within your procedural macro, and report the error directly using compile_error!.

Maybe you don't quite understand what I need, my comment! actually runs at compile time and does not generate run-time code.
I can get the correct span in comment!
But the println! code generated by reorganizing the contents of the span can be corrupted by user input errors.
I want to explicitly indicate where this part of the error occurred, but I have to validate all input parameter placeholders.
These formats! already provide, but there is no public API.
That's why I want try_format!.

@kennytm
Copy link
Member

kennytm commented Dec 17, 2021

sounds like what you really want is a proc_macro API compiling the format string, similar to #3200.

@nielsle
Copy link

nielsle commented Dec 17, 2021

As far as I can see the 'const_format' crate has a macro that produces a const function that can return an error at compile-time if the arguments do not match. But it requires the 'const_mut_refs' feature which is only available on unstable

https://docs.rs/const_format/latest/const_format/macro.try_.html

@cheeroncode
Copy link
Author

sounds like what you really want is a proc_macro API compiling the format string, similar to #3200.

It's similar in terms of ease of use.
It is easier to complete my requirements by calling cargo check and then handling the returned errors.
Currently, when a user enters incorrect data, I manually encode to validate the placeholder;
Repeat the part of the format! validation placeholder.
Then a simple error message at compile time;
In proc macro, if there is such an API, it checks the regenerated code and returns any errors found.
That would be perfect.

@cheeroncode
Copy link
Author

As far as I can see the 'const_format' crate has a macro that produces a const function that can return an error at compile-time if the arguments do not match. But it requires the 'const_mut_refs' feature which is only available on unstable

https://docs.rs/const_format/latest/const_format/macro.try_.html

@nielsle Thank you, they don't apply to me, my requirement is that I get the compiler's error in the Proc macro and decide how to deal with it, rather than having the compiler report it directly.

@peepo5
Copy link

peepo5 commented Apr 20, 2022

This may be a good idea :>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants