Dart switch statement - Case expressions must be constant - flutter

I looked at a few questions regarding this for other languages and some suggest using final but that doesn't seem to work with Dart.
I'm passing in arguments so surely the switch statement cannot contain constants only? A switch statement, much like an if statement is asking if it is or not..ie it's unknown so I don't see how a switch statement can be useful if they have to be constants...?
setCategory(arga, argb) {
int result;
switch (true) {
case (arga >= 0 && arga < 3 && argb < 35):
result = 16;
break;
case (arga >= 0 && arga < 3 && argb >= 35):
result = 15;
break;
etc
It's returning the error Case expressions must be constant regarding the values arga and argb in the case expressions. What's the best way to remedy this or do I have to use an if statement?

The switch case expressions must be constants for sure.
You have to use if/then chains to do multiple tests based on non-constant values.
You cannot use arguments from the surrounding function in a switch case. What you are trying to do here is not supported by the Dart switch statement.
The Dart switch statement is deliberately kept very simple, so that a compiler can know all the possible cases at compile-time. That's why they must be compile-time constants.
Switch statements are still useful for some kinds of switching, like on enums:
enum Nonse { foo, bar, baz; }
String fooText(Nonse non) {
switch (non) {
case Nonse.foo: return "foo";
case Nonse.bar: return "bar";
case Nonse.baz: return "baz";
}
throw ArgumentError.notNull("non");
}
You can also switch over constant string values or integer values.

There are some rules for Switch Case
The default case is optional.
All case expression must be unique.
The case statements can include only constants. It cannot be a variable or an expression.
The data type of the variable and the case expression must match.
There can be any number of case statements within a switch.
you should use 'If Else' statement

Switch statement requires a constant -> it means already initialized variable with final value
switch (expression) {
case ONE : {
statement(s);
}
break;
case TWO: {
statement(s);
}
break;
default : {
statement(s);
}
}

for(int x=0; x<1; x++){
if(ing5<8 && toWhatDegreeRand<=10 && toWhatDegreeRand>7){
toWhatDegreeRand=9;
}
if(ing4<8 && toWhatDegreeRand<=7 && toWhatDegreeRand>5){
toWhatDegreeRand=6;
}
if(ing3<8 && toWhatDegreeRand<=5 && toWhatDegreeRand>3){
toWhatDegreeRand=4;
}//....
}
It can be a little useful.

Related

Is my code complex enough for switch statement usage in Swift

From Swift documentation:
Typically, you use the if statement to evaluate simple conditions with only a few possible outcomes. The switch statement is better suited to more complex conditions with multiple possible permutations and is useful in situations where pattern matching can help select an appropriate code branch to execute.
I'm trying to decide on if I should use the switch or if/else statements based on if I have a complex condition. So my question is, is my condition complex or not.
Here is an example of what I have:
var sampleVar = Measurement(value: amount, unit: UnitLength.megameters)
if(type == "inches"){
//do something
}
else if...
I have between 5 to 15 possible conditions I'm checking for so would that make it complex enough to justify using a switch statement? Or is complexity based on the condition and not how many conditions there are?
The ultimate test is to just write out both and compare.
When switch is better
When you're dealing with a situation that favours a switch over an if/else ladder, you code will look like:
if something == 1 {
foo()
} else if something == 2 {
bar()
} else if something == 3 {
baz()
} else {
quux()
}
As you can see, all the bracketing, repeated keywords (else, if) repeated operators == and repeated instance of the same identifier (something) add a bunch of noise with really little value. Compare to the switch:
switch something {
case 1: foo()
case 2: bar()
case 3: baz()
default: quux()
}
When if/else if/else is better
You'll find yourself writing a switch where the switched variable isn't really being matched much, but instead you have a bunch of where clauses that check a lot of unrelated conditions. Compare:
switch something {
case 1: foo()
case _ where case2_1() || case2_2(): bar()
case _ where case3(): baz()
case _ where case4(): quux()
}
vs.
if something == 1 || case1() { foo() }
else if case2_1() || case2_2() { bar() }
else if case3() { baz() }
else if case4() { quux() }
Don't forget about polymorphism!
Whenever possible, try to break up complex switching logic into dynamic calls to methods on an object. This allows you to separate the logic of each case into a separate class, where all related logic can be grouped.

Swift switch statement considered all cases of Int, but compiler still display error

I understand switch statement in Swift must be exhaustive, otherwise we have to provide an default case. I saw the code below online, the switch statement already have covered all cases in Int, but the compiler still display error message that switch must be exhaustive, consider adding a default clause. Is there something I'm missing?
extension Int {
enum Kind {
case Negative, Zero, Positive
}
var kind: Kind {
switch self {
case 0:
return .Zero
case let x where x > 0:
return .Positive
case let x where x < 0:
return .Negative
}
}
}
Update for Swift 3: Swift 3 introduced ClosedRange which makes
it possible to define a range like 1...Int.max including the
largest possible integer (compare Ranges in Swift 3). So this compiles and works as expected,
but still requires a default case to satisfy the compiler:
extension Int {
enum Kind {
case negative, zero, positive
}
var kind: Kind {
switch self {
case 0:
return .zero
case 1...Int.max:
return .positive
case Int.min...(-1):
return .negative
default:
fatalError("Oops, this should not happen")
}
}
}
There are other bug reports where the Swift compiler does not
correctly determine the exhaustiveness of switch-statements, such
as https://bugs.swift.org/browse/SR-766, where Apple engineer Joe Groff
commented:
Unfortunately, integer operations like '...' and '<' are just plain functions to Swift, so it'd be difficult to do this kind of analysis. Even with special case understanding of integer intervals, I think there are still cases in the full generality of pattern matching for which exhaustiveness matching would be undecidable. We may eventually be able to handle some cases, but there will always be special cases involved in doing so.
Old answer: The compiler is not so smart to recognize that you have covered
all possible cases. One possible solution is to add a default
case with a fatalError():
var kind: Kind {
switch self {
case 0:
return .Zero
case let x where x > 0:
return .Positive
case let x where x < 0:
return .Negative
default:
fatalError("Oops, this should not happen")
}
}
Or make case 0: the default case:
var kind: Kind {
switch self {
case let x where x > 0:
return .Positive
case let x where x < 0:
return .Negative
default:
return .Zero
}
}
(Remark: I initially thought that the following would work correctly
without needed a default case:
var kind: Kind {
switch self {
case 0:
return .Zero
case 1 ... Int.max:
return .Positive
case Int.min ... -1:
return .Negative
}
}
However, this compiles, but aborts at runtime because you cannot
create the range 1 ... Int.max. More information around this
problem can be found in the article Ranges and Intervals in Swift.)

Assign conditional expression in Swift?

Is there a way in Swift to assign conditional expressions similar to this
let foo = if (bar == 2) {
100
} else {
120
}
(or with a switch case).
(Don't want to have to use ternary operator for this).
This kind of assignement is good for functional style / immutability. The expressions have a return value in this case.
Note: it's a general question, this is just simplified example, imagine e.g. a switch case with a lot of values, pattern matching, etc. You can't do that with ternary operator.
Btw also note that there are languages that don't support ternary operator because if else returns a value, so it's not necessary, see e.g. Scala.
You can use a closure to initialize an immutable:
let foo: Int = {
if bar == 2 {
return 100
} else {
return 120
}
}()
The advantage of using a closure is that it's a function, so you can use any complex logic inside, implemented in a clean way and not through nested ternary operators. It can be a switch statement, it can be obtained as the return value of a function followed by some calculations, it can be a pattern matching case, it can be a combination of them all, etc.
Said in other words, it's the same as initializing with the return value of a function, but the difference is that the function is inline and not somewhere else, with readability benefits.
Just for completeness, if the variable is mutable, you can use deferred initialization:
var foo: Int
// Any statement here
if bar == 2 {
foo = 100
} else {
foo = 120
}
// Other statements here
myFunc(foo)
so you can declare a mutable variable, and initialize it anywhere in the same scope, but before using it the variable must be initialized.
Update: Since Swift 2.0, deferred initialization also works with immutables.

How do I best handle returning a specific object depending on certain conditions when there's no real "else" case to cover everything?

I want to create and return an object in a specific method. I get passed an integer in the method, and I know it will be 0, 1 or 2.
So fundamentally I would structure it like:
if num == 0 {
return 12
} else if num == 1 {
return 24
} else if num == 2 {
return 36
}
Which I know would cover every circumstance.
But the compiler would complain that this doesn't cover every specific circumstance and I'd get an error. I need an else statement of some sort.
But what do I put as the else statement? I can't think of anything valid that would make sense. I could just change the last condition to an else, but it's not as clear later that it actually refers to a 2. It doesn't seem maintainable.
What should I do?
Simply remove the if from the last statement and leave the else only:
if num == 0 {
return 12
} else if num == 1 {
return 24
} else {
return 36
}
But I'd rather use a switch statement:
switch(num) {
case 0: return 12
case 1: return 24
case 2: fallthrough
default: return 36
}
Here the 2 case is handled by the default case, using the fallthrough keyword
An alternative way to achieving the same, taking into account that the possible values are consecutive and starting from 0, is using an array:
let values = [12, 24, 36]
return values[num]
If the function is a class/struct method, the values array can be defined as a static (class) property outside the function, just to avoid instantiating it for each function call.
Note that this solution generates a runtime exception if the num value is not in the [0, 2] range
You can't eat your cake and have it to. If it obvious then an else is fine. If it is not obvious, then you could use an enumeration so that the three options are unambiguously constrained.
For the former, you could use a trailing comment and assertion - that is probably the simplest:
assert(num >= 0 && num <= 2)
if num == 0 {
return 12
} else if num == 1 {
return 24
} else { // case that num == 2
return 36
}
or use an enum type in a switch statement, and the compiler will know that there are only three cases and that you've covered them all. You can use the toRaw() method as needed and base your enum type on an Int to facilitate that.
I imagine your actual case is a little more complicated than the one you presented, but the approaches remain unchanged.
i would do case with exception
case num of
0 -> ...
1 -> ...
2 -> ...
otherwise -> throw exception('unsupported value')
end
compiler will be happy and your code will be clean. in some languages you can go even further and change num to Enumerator. then you will have same structure of case but withoutl the otherwise part. and compiler will warn you every time you decide to add new value for your enumerator but forget to add the new case in your function

Why can't I use an NSInteger in a switch statement?

Why doesn't this work:
NSInteger sectionLocation = 0;
NSInteger sectionTitles = 1;
NSInteger sectionNotifications = 2;
switch (section) {
case sectionLocation:
//
break;
case sectionTitles:
//
break;
case sectionNotifications:
//
break;
default:
//
}
I get this compile error:
error: case label does not reduce to an integer constant
Is it not possible to use NSInteger's like this? If so, is there another way to use variables as cases in a switch statement? sectionLocation etc. have variable values.
The problem isn't the scalar type, but that the case labels may change value when they are variables like that.
For all intents and purposes, the compiler compiles a switch statement as a set of gotos. The labels can't be variable.
Use an enumerated type or #defines.
The reason is that the compiler will often want to create a 'jump table' using the switch value as the key into that table and it can only do that if it's switching on a simple integer value. This should work instead:
#define sectionLocation 0
#define sectionTitles 1
#define sectionNotifications 2
int intSection = section;
switch (intSection) {
case sectionLocation:
//
break;
case sectionTitles:
//
break;
case sectionNotifications:
//
break;
default:
//
}
The problem here is you are using variables. You can only use constants in switch statements.
Do something like
#define SOME_VALUE 1
or
enum Values {
valuea = 1,
valueb = 2,
...
}
And you will be able to use valuea and so forth in your switch statement.
If your case values truly change at runtime, that's what the if...else if...else if construct is there for.
or just do this
switch((int)secion)