I think the issue is the ambiguity of this impl in the standard library: Option as From<T>
Moving the into() function to the caller allows you to add it only when needed. Updated playground
And even with this change, the final None value needs the turbofish syntax to specify which type of None::<T> it is.
Edit: I originally thought it'd be a confusion on some Option<U> as From<Option<T>> where U: From<T> impl, but I couldn't find that conversion on the docs.
Hey @danjl1100, thanks. Obviously I'm trying to avoid the explicit conversions as I want to use this in a builder pattern and take different inputs seamlessly.
I think, though, if I had to be explicit about anything, it would be the String, not the Option, like:
fn do_something<S>(s: S)
where
S: Into<Option<String>>,
{
let opt: Option<String> = s.into();
println!("{:?}", opt);
}
fn main() {
let s = String::from("this");
do_something(s.clone());
do_something("that".to_string());
do_something(Some(s));
do_something(Some("then".to_string()));
do_something(None);
}
But I'm still hoping to figure out the way I was originally trying to do it!
A custom trait with no blanket implementations works for at least the four cases you've presented... except as @danjl1100 stated, when you pass in None, because there's no way for the compiler to know if you meant None::<String> or None::<&str> (and it can't assume it doesn't matter).
So the application could call it with a String or &str literal:
cfg.value("something");
That is still the primary use for it, but now I also need to import partial configurations, that come in as options themselves, and it's getting really verbose saying:
if let Some(val) = other_cfg.value() {
cfg.value(val);
}
instead of:
cfg.value(other_cfg.value());
while also keeping the primary use case.
Plus, only taking a String doesn't give the user a chance to turn off the option by setting it back to None after it's been given a value.
And just making the fields public isn't really an option, because there are further constraints on the accepted values, and/or other transforms that are required.
If you're set on doing this with a single entry point, and if you're willing to give up all Option cases except Option<String> (or whatever your ultimate value type is), you could support multiple bare values but only Option<String>. Then None will be unambiguous. This will be less ergonomic if someone has ahold of an Option<&str> they're trying to pass in, but for programmers just typing in values, they're going to just pass "literal" and not Some("literal") anyway.
(Or you could support only Option<&str> instead if that matches your use-case better, but that introduces the potential for unnecessary cloning. Though TBH it's probably not at all an actual problem for a builder. [1])