Multiple or single return statement?

Hi,

I would like to know what is the preferred way to return a value in a function / computed variable (single / multiple return statements).

Long back I had read that it was good to have 1 return statement, so I am just wondering.

Based on my limited understanding, I think Opaque result types requires explicit return statements as some type can't be stored. So if it involves a switch statement there would be multiple return statements for opaque result types function. Please correct me if I am wrong.

So is it ok to have multiple to have return statements when the execution path is clear (like in a switch statement) or is it better to have a single return statement as much as possible ?

protocol Shape {}

struct Circle : Shape {}
struct Oval : Shape {}
struct Square : Shape {}

Single Return Statement

func f1(label: String) -> Shape {
    
    let shape : Shape
    
    switch label {
        
    case "a":
        shape = Square()
    case "b":
        shape = Circle()
    default:
        shape = Oval()
    }
    
    return shape
}

Multiple Return Statements

func f2(label: String) -> Shape {
    
    switch label {
        
    case "a":
        return Square()
    case "b":
        return Circle()
    default:
        return Oval()
    }
}

Hi @somu,

Opaque result types only hide the result type, they don't support polymorphism.

-> some Shape returns one specific Shape, you just don't declare which one (it's considered an implementation detail). But if it's a Circle, it always has to be a Circle.

-> Shape can return any Shape. It can be a Circle one time, and a Square the next time.

Both of your examples are correct but neither will work with an opaque result type because you return different types from your return statements.

Thanks @svanimpe, yes you are correct both of what is in the original post wouldn't work with Opaque result types as they return different types.

The reason why I said opaque result types is because, if I were to use opaque result types, I would have to have multiple return statements if it depended on a switch statement as shown below.

In the example shown below based on my understanding it wouldn't be possible to store some Shape and have 1 return statement.

protocol Shape : Equatable { var size : Int { get } }

struct Circle : Shape { var size: Int }
struct Oval : Shape { var size : Int }
struct Square : Shape { var size: Int  }

Need to have multiple return statements (based on my understanding)

func f2(label: String) -> some Shape {
    
    switch label {
        
    case "a":
        return Square(size: 1)
    case "b":
        return Square(size: 2)
    default:
        return Square(size: 3)
    }
}

A single return statement should work, but you may have to be concrete about the type of your constant:

func f2(label: String) -> some Shape {
    
    let result: Square
    
    switch label {
    case "a":
        result = Square(size: 1)
    case "b":
        result = Square(size: 2)
    default:
        result = Square(size: 3)
    }
    
    return result
}

I'm guessing you can't declare result as some Shape because the compiler can't infer what "some Shape" you mean if there's no initialisation value?

2 Likes

Thanks @svanimpe nice explanation, makes sense.

Like you said, the compiler might not be able to infer the type and assignment might open the door to any type conforming to Shape.

I think I might have missed something really obvious, I could have just written as below:

func f3(label: String) -> some Shape {
    
    let square : Square
    
    switch label {
        
    case "a":
        square = Square(size: 1)
    case "b":
        square = Square(size: 2)
    default:
        square = Square(size: 3)
    }
    
    return square
}

Question:

So now back to the original question, is it ok to have multiple return statements in a function as long as it is readable like in a switch statement ?

There is never anything wrong with having multiple return statements in a function, unless your particular style guide prohibits it.

Any of the ways you have expressed the switch are fully valid in general Swift. Some things like guard statements actually require using multiple return statements, and The Swift Programming Language reference is full of examples where multiple return statements are used (such as here, here, here or here). You can trust the compiler to warn you if you ask it to do something unreasonable, such as forget to return at all, or attempt to keep doing things after returning.

If team members, employers or teachers are telling you to reduce your return statements, you will have to ask them which patterns they do not like and why. The rest of us cannot read their minds.

3 Likes

Thanks @SDGGiesbrecht, That's a nice way to sum it up, yeah like you pointed, in a lot of places multiple return statements are used both out of necessity (guard) and otherwise. Like you said may be understanding the reasoning behind the approach would be more important on which way to go or it just might come down to personal preference.

Having a single exit point can help during debugging, but imho the most important aspect is consistency:
When one case in a switch exits the function, the other cases should do so as well, so that there are no "hidden" returns you may not see at first sight.

Especially with void functions, careless use of return is basically the same as using goto, which Swift didn't adopt for good reasons.

1 Like

Thanks @Tino, that's a good point, will try to keep it consistent across the project. So far I had been having 1 return statement but most examples that I saw had multiple and hence the question.

Also I (incorrectly) thought opaque return types needed to have separate return statements which led to more doubts. Thanks to @svanimpe for pointing it out that it is possible to have 1 return statement even if a switch was used with opaque return types.