Replacement for goto statement in Swift
Asked Answered
I

8

9

How can I transfer control to a specific line in Swift code?

In Objective-C I would do something like the following, using goto

if(a==b)
{
    goto i123;
}
else
{
    goto i456;
}
NSLog(@"the not reachable point");
i123:
NSLog(@"line 123 is here");
int j = 5;
int x = 2+j;
i456:
NSLog(@"line 456 is here");

The only control transfer statements in Swift I could find were continue, break, fallthrough, and return

continue and break only work with loops; return and fallthrough don't transfer control this way.

What can I use?

EDIT:-

Julien__'s answer didn't actually solve my problem but it could be the only available option right now. so i have accepted the answer by Julien__

Ignition answered 8/2, 2015 at 18:32 Comment(5)
OMG. Certainly there are cases where a goto is nice. But generally your code looks like FORTRAN from the 50s.Somatic
This can easily cause undefined behavior, e.g. if you use the value of x after jumping to i456:. One goal of the Swift language was that the compiler can check if all variables are initialized before being used.Delrosario
I would say "use functions". goto was originally a workaround for a program without subroutines.Unguinous
they say that using of goto is bad practices - in any language you make the code less readableMudlark
You can make more money for your family and yourself by coding faster using tricks of the trade like judicious use of goto. (says California grandpa in his 50th year of full-time programming)Creuse
B
9

Perhaps a switch statement ?

switch (a==b){
default:
    NSLog(@"the not reachable point");
    fallthrough­
case true:
    NSLog(@"line 123 is here");
    int j = 5;
    int x = 2+j;
    fallthrough­
case false:
    NSLog(@"line 456 is here");
}

EDIT : Here is how you could go backward.

let START = 0
let STOP  = -1
var label = START

while(label != STOP){
    switch (label){

    default:
        label = START­

    case START:
        NSLog(@"the not reachable point");
        if a==b {
            label = 123
        } else {
            label = 456
        }

    case 123:
        NSLog(@"line 123 is here");
        int j = 5;
        int x = 2+j;
        fallthrough­

    case 456:
        NSLog(@"line 456 is here");
        fallthrough

    case STOP:
        label = STOP
    }
}

Wrap your code in a giant (but well organized) switch statement. You could even create a function named goto in order to modify the value of the label var.

Biddle answered 8/2, 2015 at 18:34 Comment(10)
why -1 ? could you at least give a reason ?Biddle
actually my original code in objective is about 1500 lines long in which i am using goto statement very frequently. i have only used code in my question as example. btw, I have already thought about switch. but the problem with this is, it will fail when i will go back and forth using goto statement.Ignition
@DemonSOCKET: I see your point. But here the problem is not that Swift has not a GOTO equivalent. The problem is that you are trying to use a paradigm (GOTO) that belongs to another era. I understand you have many lines of code that need to be ported to Swift but you should try to rewrite that code with a more modern approach. I would be very surprised if Swift had the GOTO equivalent you are looking for. IMHO.Yolande
This only allows for forward goto. You can't jump backwards using switch.Ulna
@NikolaiRuhe: I really don't think Apple inserted a GOTO instruction inside Swift and just renamed it. This paradigm simply is not available in Swift. And IMHO this is a good thing.Yolande
@appzYourLife I totally agree. And that's the correct answer, b.t.w.Ulna
@NikolaiRuhe I think you can using while. See my edit.Biddle
@Biddle : I am starting to have a feeling that i am gonna have to stick with objective c for a while longer. its better if i don't port my code into swift. anyways I really appreciate the helpIgnition
Historically, the method you describe was used for the first proof that any program can be written without gotos. Except they didn't use a switch statement but a long chain if label == 1.. else if label == 2 .. else if label == 3...Omnirange
Switch statements can't run across multiple threads at once. GoTo allows you to manually control position in code while you jump from one thread to the next. (e.g. using GCD for delays in your script).Raptorial
U
8

Ummm..... Swift actually support label which can be use as goto for control flow but less flexible. The following code is from Swift the programming language :

gameLoop: while square != finalSquare {
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }

    switch square + diceRoll {
    case finalSquare:
    // diceRoll will move us to the final square, so the game is over
        break gameLoop
    case let newSquare where newSquare > finalSquare:
    // diceRoll will move us beyond the final square, so roll again
    continue gameLoop
    default:
    // this is a valid move, so find out its effect
        square += diceRoll
        square += board[square]
    }
}
print("Game over!")
Univalve answered 27/6, 2016 at 14:25 Comment(3)
A full discussion appears in The Swift Programming Language (Swift 4.2): Labeled Statements [accessed 13 Sept 2018]Grasso
break loopLabel and/or continue myLoopLabel??? That is just a copy of other high-level language's solution for the problem: "how to break outer loop from inner loop."Hemline
But a very optimistic person may consider it a backward-goto ;-)Hemline
S
2

It seems as if Swift doesn't want anyone to use goto statements. Probably to avoid spaghetti code that would be too hard to follow in the future.

One possible alternative is to use functions. Functions have names which have meaning and are easier to understand than mere line numbers.

Samson answered 9/12, 2015 at 17:17 Comment(0)
O
2

For the sake of posterity:

  • The article goto in Swift clearly exemplifies how to implement goto style functionality, including warnings on why not to use them and a rationale for their absence in the language.

  • The article author also made it available as a Swift package called Goto.swift on GitHub.

Ockham answered 2/4, 2016 at 16:28 Comment(0)
Y
1

What about this?

var j : Int?
var x : Int?

if a == b {
    println("line 123 is here")
    j = 5
    x = 2 + j!
}
println("line 456 is here")
Yolande answered 8/2, 2015 at 18:38 Comment(1)
Probably because this does not answer the question (How to transfer control to a specific line?). It's just a workaround for this specific case.Ulna
C
1

In languages where it is available I don't think GoTo is always a bad thing. I'd never use it to jump up, down and around within a function. That causes no end of confusion. But I do like to use GoTo to give me a common exit point. Here's an example of what I mean (pseudo code) something like this:

func SomeFunction() -> Bool
{
    var iReturnValue = false;

    // Do some processing

    if(SomeCondition == true)
    {
        // return true;
        // No. Instead of having a return statement here.
        // I'd rather goto a common exit point.

        iReturnValue = true;
        goto ExitPoint;
    }

    // Do Some More processing

    if(SomeOtherCondition == SomeResult)
    {
        iReturnValue = false;
        goto ExitPoint;
    }

    //
    // More processing
    //

ExitPoint:
    // By having a common exit point I can do any
    // cleanup here and I've got a single return point
    // which I think makes my code easier to read.

    return iResultValue;
}

I know I could achieve the same thing with a few well placed braces but I just find a well used Goto makes life much simpler.

Commit answered 14/5, 2016 at 18:14 Comment(1)
Looks like guard is what you want.Schuck
H
0

Here is a closure ({...}()) approach:

let done = false
while !done {
    {
        for formantA in editedFormants {
            for formantB in editedFormants {
                if abs(formantA - formantB) < MIN_DISTANCE {
                    let newFormant = (formantA + formantB) / 2
                    editedFormants.removeAtIndex(editedFormants.indexOf(formantA)!)
                    editedFormants.removeAtIndex(editedFormants.indexOf(formantB)!)
                    editedFormants.append(newFormant)
                    editedFormants = editedFormants.sort()
                    return
                }
            }
        }
        done = true
    }()
}
Humanist answered 26/1, 2016 at 4:57 Comment(0)
C
0

I am sure your code could be refactored to avoid the use of goto. Alternatively, you can use functions within functions and still have access to outer parameters. For example,

func foobar(a: Int, b: Int) {

    func i123() {
        let j = 5
        let x = 2+j
        print("i123 x=\(x) b=\(b)")
    }

    func i456() {
        print("i456")
    }

    if a == b {
        i123()
    } else {
        i456()
    }
}
Cleres answered 6/2, 2016 at 20:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.