Skip to content

Implement TryFrom<&str> for numerical types, bool and char #132

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

Closed
grego opened this issue Nov 11, 2022 · 8 comments
Closed

Implement TryFrom<&str> for numerical types, bool and char #132

grego opened this issue Nov 11, 2022 · 8 comments
Labels
api-change-proposal A proposal to add or alter unstable APIs in the standard libraries T-libs-api

Comments

@grego
Copy link

grego commented Nov 11, 2022

Proposal

Implement the trait TryFrom<&'a str> for numerical types, bool and char.

Problem statement

The given types implement the FromStr trait; but that can't be used in contexts that require generic TryFrom interface, which is the standard fallible conversion in Rust.

Motivation, use-cases

struct Dummy<T>(T);

impl<'a, T: TryFrom<&'a str>> TryFrom<&'a str> for Dummy<'a> {
    type Error = T::Error;

    fn try_from(s: &'a str) -> Result<Self, Error> {
        Dummy(T::try_from(s)?)
    }
}

Then, one could just do

let dummy: Dummy<u64> = s.try_into();

Solution sketches

Mimic the FromStr implementation for the given types.

Links and related work

A PR implementing this is available here.

@grego grego added api-change-proposal A proposal to add or alter unstable APIs in the standard libraries T-libs-api labels Nov 11, 2022
@cuviper
Copy link
Member

cuviper commented Nov 11, 2022

Your motivation demonstrates how to use this, but not really why you would want to do it this way. Dummy could just as easily implement FromStr itself. Do you have a real-world motivation where that wouldn't work?

@grego
Copy link
Author

grego commented Nov 11, 2022

Dummy could be a struct from another crate, so implementing another trait would not be an option.
Also, when doing zero-copy parsing, one wants the struct to be generic over TryFrom<&'a str> as that enables to use the lifetime 'a in the resulting type and prevent copying. Dummy might have other fields that are only possible to be parsed in the zero-copy fashion.

@dtolnay
Copy link
Member

dtolnay commented Nov 11, 2022

I am skeptical that we would want to add any of these impls, especially as your last comment explicitly refers to parsing. Parsing and From conversions are semantically different things.

[the difference]

I've found it's easy to understand the difference in the context of something like serde_json::Value:

let v: serde_json::Value = "[1, 2, 3, 4]".parse()?;

The above uses str::parse whose behavior is defined by a FromStr impl. For JSON this is going to parse JSON and you'll get a Value::Array.

On the other hand:

let v: serde_json::Value = true.into();
let v: serde_json::Value = 100i32.into();
let v: serde_json::Value = "string".into();

The above uses Into::into, with behavior defined by a From impl. You'll get a Value::Bool, Value::Number, and Value::String respectively.

There is a blanket impl for TryFrom in the standard library where if an Into impl exists for a particular conversion, then a TryFrom impl is automatically implemented for that conversion and an infallible error type. So the existence of From to make the above work also implies the existence of TryFrom<&str> for serde_json::Value with behavior that is definitely not parsing.


If the motivation for this feature request is that having a lifetime available (in contrast to FromStr) would allow zero-copy parsing abstractions to be built around the TryFrom trait instead of their own trait, that is not compelling to me, since TryFrom is semantically different from parsing.

@grego
Copy link
Author

grego commented Nov 12, 2022

I did not want to say the TryFrom implementation should be used as the parser itself. (Maybe I didn't provide the clearest example.)
Rather, it might be that some parser determines that some substring in the string that is being parsed should represent the data of type T, where T is implementation defined.
This is contrary to the example you provided, where serde_json::Value doesn't have an a priori known JSON data type and it is determined by parsing the string.

@SUPERCILEX
Copy link

Why just primitive types? If your argument holds, shouldn't we add a blanket impl that makes TryFrom<&str> work on all FromStr? In general I agree with the sentiment that From is for conversion and FromStr is for parsing.

@grego
Copy link
Author

grego commented Dec 5, 2022

No. FromStr doesn't take the lifetime into account, so it can't always be the same.

@SUPERCILEX
Copy link

Ah, bummer. Then yeah, I agree that this ACP's API would be confusing.

@clarfonthey
Copy link

I've wanted to figure out a more fleshed-out FromStr API since the biggest case against this I've seen is stuff like serde_json::Value. FromStr parses JSON from a string, but From converts a string to JSON, quoting it and escaping characters accordingly.

I don't think we should do this for that reason.

@dtolnay dtolnay closed this as completed May 30, 2023
@dtolnay dtolnay closed this as not planned Won't fix, can't repro, duplicate, stale Nov 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-change-proposal A proposal to add or alter unstable APIs in the standard libraries T-libs-api
Projects
None yet
Development

No branches or pull requests

5 participants