How to use ? and catch in Rust? - exception

It seems that ? and catch have been accepted into Rust, but I am not able to use it properly:
let x = catch {
1
}
I think this should give me Ok(1). Instead, I get the error:
error: expected identifier, found `1`
--> src/main.rs:15:9
|
15 | 1
| ^
Is this syntax not yet supported in Rust, or is there a problem with my code?

This is going to sound very dumb, because it kind of is. You need to have
#![feature(catch_expr)]
fn main() {
let x = do catch {
1
};
}
Basically, catch, like default and union, cannot be made a keyword without breaking backwards compatibility. Since Rust has tried to guarantee that any code on 1.x will still work on 1.y, introducing default, union and now catch into the grammar has been all about figuring out the conflicts. For union and default, there are none - identifiers are never expected in those positions.
For catch, catch { 1 } can also be interpreted as a struct literal. Unless you require do (a reserved keyword) occur before catch - then there is no ambiguity. Yes, this is ugly, and everyone knows it.
Hopefully these nightmares go away when 2.0 comes around and we can break backwards compatibility. Quoted from the rust-internals discussion around the grammar woes of catch:
There's a growing list of other syntax we would ultimately like to repurpose in this way, but we generally avoid breaking the ability to run code that worked on 1.x on 1.y where y > x except in cases where the code was exploiting a compiler bug or was just a terrifically rare construction. Our stability guarantees demand we wait until 2.0 for most of these.

TL;DR: The RFC is accepted, but your syntax is slightly off (unfortunately) and the feature is still gated.
See Alec's excellent answer on how to actually use catch.
I encourage you to read the full error log:
error: expected identifier, found `1`
--> <anon>:2:21
|
2 | let x = catch { 1 };
| ^
error[E0422]: cannot find struct, variant or union type `catch` in this scope
--> <anon>:2:13
|
2 | let x = catch { 1 };
| ^^^^^ not found in this scope
error: aborting due to 2 previous errors
The clue is in the second error message:
cannot find struct, variant or union type catch in this scope
which really lets us know that catch is not recognized as a keyword by the compiler.
Since catch looks like any regular word, what happens is that the compiler attempts to parse this as building a struct or enum. Indeed, the syntax for building a struct or enum is:
struct X { name: i32 }
let x = X { name: 1 };
Therefore, the compiler sees <identifier> { and expects it to be followed by a list of <identifier>: <expression>. It reads 1, which is not an identifier, and reports the error that 1 is not an identifier.

Related

OCaml Streams: exception handling or Option types?

To handle exceptional cases in OCaml, it is often possible to choose to either catch exceptions or to use the type 'a option. If I understand correctly, both choices are possible for Streams thanks to the functions Stream.next (raises an exception) and Stream.peek/Stream.junk (return 'a option).
Is there any difference between
match Stream.peek t with
| None -> ***
| Some c -> Stream.junk t; *****
and
try
let c = Stream.next t in *****
with Stream.Failure -> ***
where t is a variable of type 'a stream and *** and ***** are some sequences of expressions?
Edit 2020-12-03
Since OCaml 4.02, it is also possible to write the above as
match Stream.next t with
| c -> *****
| exception Stream.Failure -> ***
which may be preferable if further pattern matching is to be done on c, e.g.,
match Stream.next t with
| [] -> *****
| a::b -> *******
| exception Stream.Failure -> ***
(source: Xavier Leroy's slides What's new in OCaml 4.02).
There is no difference in behavior, as you note.
The version with an option type will cons up some more data, as it has to create Some x for each value returned. This will be a minor effect in general because OCaml is excellent at garbage collecting short-lived values.
I personally like using options because of the parallelism between the two cases. But using peek and junk seems quite a bit clunkier than just having a function that returns an option. So for this case I might use the exception-based handling.
In other words, it's a judgement call (in my opinion).

Value polymorphism and "generating an exception"

Per The Definition of Standard ML (Revised):
The idea is that dynamic evaluation of a non-expansive expression will neither generate an exception nor extend the domain of the memory, while the evaluation of an expansive expression might.
[§4.7, p19; emphasis mine]
I've found a lot of information online about the ref-cell part, but almost none about the exception part. (A few sources point out that it's still possible for a polymorphic binding to raise Bind, and that this inconsistency can have type-theoretic and/or implementation consequences, but I'm not sure whether that's related.)
I've been able to come up with one exception-related unsoundness that, if I'm not mistaken, is prevented only by the value restriction; but that unsoundness does not depend on raising an exception:
local
val (wrapAnyValueInExn, unwrapExnToAnyType) =
let exception EXN of 'a
in (EXN, fn EXN value => value)
end
in
val castAnyValueToAnyType = fn value => unwrapExnToAnyType (wrapAnyValueInExn value)
end
So, can anyone tell me what the Definition is getting at, and why it mentions exceptions?
(Is it possible that "generate an exception" means generating an exception name, rather than generating an exception packet?)
I'm not a type theorist or formal semanticist, but I think I understand what the definition is trying to get at from an operational point of view.
ML exceptions being generative means that, whenever the control of flow reaches the same exception declaration twice, two different exceptions are created. Not only are these distinct objects in memory, but these objects are also extensionally unequal: we can distinguish these objects by pattern-matching against exceptions constructors.
[Incidentally, this shows an important difference between ML exceptions and exceptions in most other languages. In ML, new exception classes can be created at runtime.]
On the other hand, if your program builds the same list of integers twice, you may have two different objects in memory, but your program has no way to distinguish between them. They are extensionally equal.
As an example of why generative exceptions are useful, consider MLton's sample implementation of a universal type:
signature UNIV =
sig
type univ
val embed : unit -> { inject : 'a -> univ
, project : univ -> 'a option
}
end
structure Univ :> UNIV =
struct
type univ = exn
fun 'a embed () =
let
exception E of 'a
in
{ inject = E
, project = fn (E x) => SOME x | _ => NONE
}
end
end
This code would cause a huge type safety hole if ML had no value restriction:
val { inject = inj1, project = proj1 } = Univ.embed ()
val { inject = inj2, project = proj2 } = Univ.embed ()
(* `inj1` and `proj1` share the same internal exception. This is
* why `proj1` can project values injected with `inj1`.
*
* `inj2` and `proj2` similarly share the same internal exception.
* But this exception is different from the one used by `inj1` and
* `proj1`.
*
* Furthermore, the value restriction makes all of these functions
* monomorphic. However, at this point, we don't know yet what these
* monomorphic types might be.
*)
val univ1 = inj1 "hello"
val univ2 = inj2 5
(* Now we do know:
*
* inj1 : string -> Univ.univ
* proj1 : Univ.univ -> string option
* inj2 : int -> Univ.univ
* proj2 : Univ.univ -> int option
*)
val NONE = proj1 univ2
val NONE = proj2 univ1
(* Which confirms that exceptions are generative. *)
val SOME str = proj1 univ1
val SOME int = proj2 univ2
(* Without the value restriction, `str` and `int` would both
* have type `'a`, which is obviously unsound. Thanks to the
* value restriction, they have types `string` and `int`,
* respectively.
*)
[Hat-tip to Eduardo León's answer for stating that the Definition is indeed referring to this, and for bringing in the phrase "generative exceptions". I've upvoted his answer, but am posting this separately, because I felt that his answer came at the question from the wrong direction, somewhat: most of that answer is an exposition of things that are already presupposed by the question.]
Is it possible that "generate an exception" means generating an exception name, rather than generating an exception packet?
Yes, I think so. Although the Definition doesn't usually use the word "exception" alone, other sources do commonly refer to exception names as simply "exceptions" — including in the specific context of generating them. For example, from http://mlton.org/GenerativeException:
In Standard ML, exception declarations are said to be generative, because each time an exception declaration is evaluated, it yields a new exception.
(And as you can see there, that page consistently refers to exception names as "exceptions".)
The Standard ML Basis Library, likewise, uses "exception" in this way. For example, from page 29:
At one extreme, a programmer could employ the standard exception General.Fail everywhere, letting it carry a string describing the particular failure. […] For example, one technique is to have a function sampleFn in a structure Sample raise the exception Fail "Sample.sampleFn".
As you can see, this paragraph uses the term "exception" twice, once in reference to an exception name, and once in reference to an exception value, relying on context to make the meaning clear.
So it's quite reasonable for the Definition to use the phrase "generate an exception" to refer to generating an exception name (though even so, it is probably a small mistake; the Definition is usually more precise and formal than this, and usually indicates when it intends to rely on context for disambiguation).

Is there any advantage in specifying types of variables and return type of functions?

I always set the types of my variables and functions, a habit I brought from my Java learning, seems the right thing to do.
But I always see "weak typing" in other people's code, but I can't disagree with that as I don't know what are the real advantages of keep everything strong typed.
I think my question is clear, but I gonna give some examples:
var id = "Z226";
function changeId(newId){
id = newId;
return newId;
}
My code would be like this:
var id:String = "Z226";
function changeId(newId:String):String{
id = newId;
return newId;
}
Yes, the big advantanges are:
faster code execution, because the runtime know the type, it does not have to evaluate the call
better tool support: auto completion and code hints will work with typed arguments and return types
far better readability
You get performance benefits from strongly typing. See http://gskinner.com/talks/quick/#45
I also find strongly typed code to be much more readable, but I guess depending on the person they may not care.
As pointed out by florian, two advantages of strongly typing are that development tools can can use the information to provide better code-hinting and code-completion, and that type, as an explicit indicator of how the variable or method is intended to be used, can make the code much easier to understand.
The question of performance seems to be up for debate. However, this answer on stackoverflow suggests that typed is definitely faster than untyped in certain benchmark tests but, as the author states, not so much that you would notice it under normal conditions.
However, I would argue that the biggest advantage of strong typing is that you get a compiler error if you attempt to assign or return a value of the wrong type. This helps prevent the kind of pernicious bug which can only be tracked down by actually running the program.
Consider the following contrived example in which ActionScript automatically converts the result to a string before returning. Strongly typing the method's parameter and return will ensure that the program will not compile and a warning is issued. This could potentially save you hours of debugging.
function increment(value) {
return value + 1;
}
trace(increment("1"));
// 11
While the points in the other answers about code hinting and error checking are accurate, I want to address the claim about performance. It's really not all that true. In theory, strong type allows the compiler to generate code that's closer to native. With the current VM though, such optimization doesn't happen. Here and there the AS3 compiler will employ an integer instruction instead of a floating point one. Otherwise the type indicators don't have much effect at runtime.
For example, consider the following code:
function hello():String {
return "Hello";
}
var s:String = hello() + ' world';
trace(s);
Here're the AVM2 op codes resulting from it:
getlocal_0
pushscope
getlocal_0
getlocal_0
callproperty 4 0 ; call hello()
pushstring 12 ; push ' world' onto stack
add ; concatenate the two
initproperty 5 ; save it to var s
findpropstrict 7 ; look up trace
getlocal_0 ; push this onto stack
getproperty 5 ; look up var s
callpropvoid 7 1 ; call trace
returnvoid
Now, if I remove the type indicators, I get the following:
getlocal_0
pushscope
getlocal_0
getlocal_0
callproperty 3 0
pushstring 11
add
initproperty 4
findpropstrict 6
getlocal_0
getproperty 4
callpropvoid 6 1
returnvoid
It's exactly the same, except all the name indices are one less since 'String' no longer appears in the constant table.
I'm not trying to discourage people from employing strong typing. One just shouldn't expect miracle on the performance front.
EDIT: In case anyone is interested, I've put my AS3 bytecode disassembler online:
http://flaczki.net46.net/codedump/
I've improved it so that it now dereferences the operands.

Defining error codes

Is there a "right way" to define error codes? I mean, I've built a library a while ago that throws custom exceptions, but I targeted the custom error messages to a developer's standpoint. Now I'm wrapping up the GUI and when I catch those exceptions, I need more user friendly messages. That's not a problem in itself, but let's say, I have my ReceiverNotAvailableException exception and NoMessageReceivedException. To me, as a developer, they mean completely different things and have different inner messages, but to the end-user they just mean "User not found". I'd like to display something like "User not found (error X)" where X varies depending on which exception is raised - pretty commonplace if you ask me.
My question is: should I go with X=1, 2 and so forth depending on what kind of exception or should I opt for something more complicated for whatever reason? I know it sounds like a dumb question, but I'd really like to know what the "best practice" (I'm not so fond of the term) is in this case.
BTW, of course I'd have a table mapping each code to its corresponding exception, whichever the case is.
If your exceptions can't overlap, then going with a HashTable[ExceptionName] = "Error Message" looks like a sane option. If they can you can use something like the following:
The standard way to define message codes that can overlap (this is, occur at the same time) is to use powers of two:
define ERROR_SYSTEM_DOWN 1
define ERROR_DATABASE_UNREACHABLE 2
define ERROR_SPACE_UNAVAILABLE 4
define ERROR_DISK_DIED 8
and so on. Then, in code you can do
if (disk_died() && no_space()) {
int errorCode = ERROR_DISK_DIED | ERROR_SPACE_UNAVAIABLE; //Binary or
return errorCode;
}
Finally, on the receiving end you can:
if (errorCode & ERROR_DISK_DIED == ERROR_DISK_DIED) { //Binary and
//then, at least, disk died. You can check for the rest in the same way
}
Explanation:
ERROR_SYSTEM_DOWN = 0001
ERROR_DATABASE_UNREACHABLE = 0010
ERROR_SPACE_UNAVAILABLE = 0100
ERROR_DISK_DIED = 1000
Then
1000 | 0100 = 1100
and, on the checking code
1100 & 0100 = 0100
Now, if you are using exceptions you can use the same approach, bubbling up the errorCode as long as exception occur. Although this idiom is more common in C.

Catching Exception, do's and dont's

Is it always a bad programming technique to leave a catch block empty when using a try-catch block?
In cases where I am expecting an exception, for example, I am reading 10 values from a file...and converting each value into a String. There is a possibility that one of the 10 values can be a null, but I don't want to stop my execution at that point and rather continue (obvious use of try catch)
A lame example of what I am trying:
String _text = textReader.ReadLine(); //Assuming this RETURNS a NULL value
try {
String _check = _text.ToString();
//Do something with _check, but it should not be NULL
}
catch (Exception)
{ //Do Nothing }
At this point, when I catch an exception:
1. I don't want to log this. Since I am expecting a buggy value.
2. I don't want to re-throw the exception up the call-stack.
3. I want to simply continue with my execution
Under these cases, is it acceptable to leave the catch empty? Or is this a complete NO-NO and there is a better way to handle this?
I presume this can be a community wiki since it also deals with programming techniques.
- Ivar
I assume that you mean
_text.ToString()
and you're concerned about the case when _text may be null?
I don't like your use of exceptions in this case. Put yourself in the mind of someone needing to maintain this code. They see:
catch (Exception) { }
Can they really deduce that all this is doing is catching the Null case? They have to consider what other Exceptions might be thrown. At the very least this raises uncertainty in the maintainer's mind.
Why could you not code:
if ( _text != null ) {
String _check = _nullValue.ToString();
}
This says exactly what you mean.
But taking this further, what does getting a value of NULL mean? You're reading a file that may have 10 values in it. I'm guessing that maybe blank line gives a null for you?
What do intend if you get:
1
2
<blank line>
4
...
10
So that's 9 good values and a blank line. What will you do if instead you get 10 good values and a blank line? 11 good values? 11 good values and a blank line?
My point is that silently ignoring oddities in user input is often a bad idea. It's quite likely that some of the cases above are actually typos. By warning in some of these cases you may be very helpful to the user. This implies that probably most odditities in input need at least some kind of count, if not an actual immediate error log.
You might, for example, emit a message at the end
Your file contained 13 lines, two lines were blank. We processed 10 valid values and ignored one.
At the very leasyt for debugging purposes you might have trace statements in the error paths.
Summary: completely ignoring Exceptions is rarely the right thing to do.
If it's expected it's not really exceptional. Is this a good use of exceptions? It may be more appropriate to test for a null value before trying to do stuff.
if(_text != null)
// do something
The best argument I liked was that the code will become highly un-maintainable. And my aim was to make my code as easy to understand as possible.
With the suggestions above, I have an army of ternary operators in place (my personal favorite over big if-else blocks).
And the code definitely looks better! and I think unless the try-catch is well documented, I don't myself see a good reason for having empty catch statements anymore.
Thanks Guys!!
-Ivar