Kotlin lang

[kotlin] 코틀린 inline과 crossinline 키워드

Razelo 2024. 4. 27. 11:01

코틀린에 흥미로운 키워드가 하나 있다. 

 

inline과 crossinline이라는건데, 오늘 처음 봤다. 

 

inline함수로 지정하면 컴파일 시 해당 함수의 바이트코드가 호출 지점에 직접 삽입된다고 한다. 

이걸 인라인화라고 한다. 그래서 함수 호출의 오버헤드를 줄일 수 있다고 한다. 

 

간단한 함수 있을때 사용하면 조금이나마 성능을 줄일 수 있을 것 같다. 

별거없다. 그냥 코드 치환이고 성능 조금 더 올리고 싶을 때 쓰면 된다. 

 

재밌는건 crossinline이라는 생소한 키워드도 있다.  

이건 inline 함수의 람다 파라미터에 사용된다. inline 함수 내부에 전달된 람다가 비지역 반환(non-local returns)를 할 수 없음을 나타낸다고 한다. 혹은 방지라는 용어가 더 알맞겠다. 

 

비지역 반환이란?

 

생소한 단어인데, inline 함수에서 lambda를 파라미터로 사용할때 람다 안에서 return문을 사용하면 해당 return이 lambda를 호출한 가장 가까운 함수로 반환된다는데 이게 비지역 반환이라고 한다. 그런데 crossinline은 이 비지역 반환을 방지하게 해준다는 거다. 그래서 return을 사용할 수 없다고 한다. 이 비지역 반환이 왜 위험한 지 잘 모른다면 이 crossinline을 왜 써야하는지를 모를 수 있다. 

 

crossinline키워드는 어디에 써야할까? 

 

비지역반환에 대한 아주 좋은 예시가 있다. 

 

fun main() {
    println("Main start")
    myFunction {
        println("Before return")
        return // 이 위치에서 비지역 반환
        println("After return") // 이 코드는 실행되지 않음
    }
    println("Main end") // 이 코드도 실행되지 않음
}

inline fun myFunction(block: () -> Unit) {
    println("Function start")
    block()
    println("Function end") // 람다에서 비지역 반환 때문에 이 코드도 실행되지 않음
}

 

 

위 코드에서 출력되는건 아래와 같다. 

 

Main start
Function start
Before return

 

 

조금 이상할 수 있다.

Function end가 출력이 안되는건 이해가 되는데, 왜 Main end는 출력이 안되는거죠 ?  

왜냐면 myFunction이 람다를 block으로 받아서 실행시키는데 이때 코드 내 return을 사용한다. 그런데 return은 람다를 호출한 함수 즉 main 함수까지의 return을 의미한다. myFunction에서만 return되는게 아니라는 뜻이다. 

 

그러니까 그냥 main에서 return 사용한거랑 똑같다고 보면 된다. 그래서 Main end까지도 출력이 되지 않고 종료되어버리는 거다. 

 

얼핏봐도 약간 이상하게 동작하는 듯한 감이 있다. 코드를 읽는 플로우상 myFunction내의 block()을 main 호출부에서 건넨 람다 코드로 치환해서 머릿속에 떠올리기 마련이기 때문에 적어도 main의 마지막 println문은 실행될거라고 생각할 수 있지만 그게 아닌거다. myFunction에서만 return되고 println("Main end")는 실행될 것 같지만 그렇지 않다. 

 

이래서 crossinline키워드가 필요하다는 것이다. 위처럼 그냥 non local return을 하게 만들지 못하게 이런 동작을 애초에 방지하게끔 return을 쓰질 못하게 막아두는 것이다. 

 

때에 따라 유용할만한 제약사항을 걸어주는 키워드가 좋긴 한데, 실제 업무에서 쓸 일은 적을 것 같다. 람다 블락을 넘겨서 처리할만한 일이 있을 것 같진 않다. 함수 내에서 유연한 동작 구현을 위해서 굳이 람다를 넘겨서 처리할만한 일이 있을까나... 

 

그리고 사실 좀 더 차분하게 생각해보면 아주 이상한 동작도 아니긴 하다.

유추해보건데, inline이 코드 치환으로 바이트 코드를 해당 호출부로 넣어버린다고 생각하면 crossinline도 비슷한 류일테니 lambda block을 넘겨준 호출부로 코드를 치환하므로 return이 해당 위치로 삽입되는거나 다름없다.

이해하면 쉬운데 그냥 보다가 헷갈릴 여지가 있어서 만들어둔 키워드인 것 같다. 

 

 

반응형