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

Reply via email to