I guess my question is, why not just the TryFrom trait in order to convert the preferred kind of literal into the desired type?
What would this allow that can't be done using that route?
Well, one disadvantage macros have is prefix vs. suffix notation: seconds!(5) or Seconds(5) (or worse, Seconds::new(5)) vs. 5s (which C++ defines in std::literals::chrono_literals).
Custom literals may not necessarily be the solution, but I would find it nice if I could write literals for unit newtypes (like Seconds, Kilometers, Radians), in a more natural form in rust.
That's what I'm currently doing (except I make the user prefix an underscore in front of the digits when the numbers get really large, because the lexer complains about numbers bigger than 2^128 even in macros).
But this makes it so that user-defined types are forever second-class citizens compared to built-in types. It would be nicer to write x + 1 rather than x + bigint!(1).
Having the suffix be a string runs into the coherence issues mentioned by @Nemo157. A simple alternative is to just look up the suffix as a type. Combining that with const functions as mentioned by @2e71828, using an inline const expression…
123u32 would desugar to const { u32::from_decimal_literal("123") }
0x123Foo would desugar to const { Foo::from_hexadecimal_literal("123") }
Admittedly, suffixes starting with a capital letter look ugly, so this would encourage the use of type names starting with a lowercase letter (or at least type aliases, e.g. type foo = Foo if your type was Foo but you wanted to be able to use the suffix foo). This may or may not be a problem.
You can already do something quite similar with extension traits. I've done it for the time crate, at least. Honestly the only thing that would be helpful here is having impl const Foo, which is already on nightly. This would allow trivial situations like mine to be done at compile-time. Combined with const blocks and/or more const evaluation for optimization, this would effectively result in custom literals.
Unfortunately... hex literals with suffixes are hard. The logical parse for that would actually be const { oo::from_hexadecimal_literal("0x123F") }. abcdef are valid hex digits so part of the number, not the suffix. (I believe this is part of why hex floats aren't allowed: suffixing them with f32 wouldn't work as expected, because those are all hex digits.)
It's not a bug, it's a feature! We want the type to be able to be deduced based on context, just like today built-in integer types are deduced based on context.
In particular this is important for the case of the empty suffix, where it would be nice if it could not only be a built-in integer, but some user defined type as well like a BigInt or a Gaussian integer.
That's already a thing being actively developed FYI, which you can use on nightly currently with the const_trait_impl feature flag. It works differently than in your example though (in a good way, I'd say). That is, what you do is just:
impl const LiterallyAnyTrait for SomeType.
Meaning trait authors don't have to do anything differently (since marking trait methods explicitly as const fn is not part of how the feature works at all), and the feature is immediately compatible with all traits ever, with the only limitation being that of people's abilities / desire to actually come up with useful const-compatible implementations of them.
Here's a playground example that IMO demonstrates why the feature would be a lot worse if it depended on a given trait to be "written as const", since nothing like ConstAdd or ConstAddAssign or what have you exists obviously.