On 12/06/2013 09:41 PM, Simon Sapin wrote:
We have some functions and methods such as
[std::str::from_utf8](http://static.rust-lang.org/doc/master/std/str/fn.from_utf8.html)
that may succeed and give a result, or fail when the input is invalid.
1. Sometimes we assume the input is valid and don’t want to deal with the error
case. Task failure works nicely.
2. Sometimes we do want to do something different on invalid input, so returning
an `Option<T>` works best.
And so we end up with both `from_utf8` and `from_utf8`. This particular case is
worse because we also have `from_utf8_owned` and `from_utf8_owned_opt`, to cover
everything.
Multiplying names like this is just not good design. I’d like to reduce this
pattern.
Getting behavior 1. when you have 2. is easy: just call `.unwrap()` on the
Option. I think we should rename every `foo_opt()` function or method to just
`foo`, remove the old `foo()` behavior, and tell people (through documentation)
to use `foo().unwrap()` if they want it back?
The downsides are that unwrap is more verbose and gives less helpful error
messages on task failure. But I think it’s worth it.
What do you think?
(PS: I’m guilty of making this worse in #10828, but I’d like to discuss this
before sending pull requests with invasive API changes.)
[A bit long, sorry, this is a topic about which i have thought for a while.]
There may be a more complicated general pattern, of all kinds of functions that
may not be able to perform their nominal task, due to invalid input, but the
client cannot know whether the input is valid without more or less reproducing
said task. Checking utf8 validity is about the same job as decoding, for
instance, to reuse your example.
Compare with a function computing the average value of a collection of numbers
(or the sum, product, std-dev, etc...) which is passed an empty collection: here
the client can know, thus:
1. if this abnormal case does not belong to the app's logic, the client should
just call the func stupidly so that the func failure is a signal of logical
error on the app side
2. if instead this case belongs to the app's logic, the client should first
check, and never call the func in this special case
Thus, despite very possible failure, there should here be only one version of
the func (no *_opt), one that stupidly fails, with a stupid error msg.
Back to the cases where the client cannot know before calling. To this category
belong a whole series of search/find functions, and many dealing with the file
system, user input, input in general. In the latter case, a func's input is in
fact not (all) provided by the client. But there is the same pattern of
anomalous cases which may, or not, belong to the app logic (1. or 2. above): is
it correct (if special or exceptional) that such file does not exist, or such
collection does not hold the searched item? Meaning, should I deal with such
cases? If not, if such a case does not belong to the application logic, again I
should stupidly call a func that stupidly fails with a stupid error msg, so I am
told, simply and early, of my logical errors. These are true errors (not
so-called exceptions), and the failure is a symptom or signal, a good thing (if,
indeed, what you want is making good software). I *want* it; and want it so!
I'm in favor of simple funcs doing simple tasks simply, and just failing in
anomalous cases, for these reasons. [1]
Remains the situation combined of such funcs, of which the client cannot know
whether they will be able to perform their task, and of abnormal cases belonging
to the logic of the app (there are also whole categories of maintenance and
safety modes here). For such situations, it looks somewhat logical to have 2
versions, I guess. Playing a bit with words:
1. when I ask to _find_ something, I take it for granted the thing is somewhere
there, and expect a location in return
2. when I ask to _search_ something, I do not take anything for granted, and
expect in return _either_ "not there!" or a location
This duality applies both to the so-called real world (despite blur natural
language word meanings) and software worlds, and extends to all cases, it seems.
We can certainly find a way, using Option or another mean, to combine both
categories of situations (1. or 2.) using a single tool, but this should be very
well done, and ensure that:
* In cases of category 1., developers are warned about their errors exactly as
if they had called a stupidly failing func.
* At the semantic (conceptual) level, the duality of categories remains somehow
preserved, including in source code.
About the latter, in particular it should be obvious in code, without knowledge
of language arcanes or weird idioms, that (or whether) the caller expects a
success unconditionally -- because (and in other words that) the anomalous case
just does not belong to this app; this is critical information to the reader.
How to do that right?
PS: I take the opportunity of again thanking the initiators of this Rust project
for their welcoming and open-mindedness to such exchanges. This is really great.
I'm not sure anymore Rust is the right language for me (maybe too complicated
and abstract for my poor and old little mind...) but go on following the mailing
list (and please keep it a mailing list! ;-) for this quality of sharing.
Denis
[1] I'm very much against complicated funcs, even more against ones that try to
guess what you want in anomolous cases (eg return 0 as sum of no number, or 1 as
product, lol!), and totally against the pretention that software should not
fail, as apparently defended by Go's designers.
This leads to illogical things, like their utf8 decoder, precisely, inserting
codes for the replacement character U+FFFD �, a *valid* code, instead of
signaling invalid source. So that you never know the input was invalid, or
whether this code belong to the source or was inserted by their func...
_______________________________________________
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev