Ключевое слово Crossline

Я прочитал этот вопрос, но у меня есть более фундаментальный вопрос относительно ключевого слова crossinline. Я не совсем уверен, какая проблема решает и как это решает.

Из Документы Kotlin,

Обратите внимание, что некоторые встроенные функции могут вызывать lambdas, переданные им как параметры не непосредственно из тела функции, а из другого контекста выполнения, такого как локальный объект или вложенная функция. В таких случаях нелокальный поток управления также не допускается в лямбда. Чтобы указать, что, параметр лямбда должен быть отмечен модификатором кросс-линии:

[Акцент добавлен]

Это утверждение неоднозначно для меня. Во-первых, у меня возникают проблемы с изображением того, что подразумевается под "такими случаями". У меня есть общее представление о том, что проблема, но не может придумать хороший пример.

Во-вторых, фраза "Чтобы указать это", можно читать несколькими способами. Чтобы указать, что? Что конкретный случай не допускается? Что это разрешено? Этот нелокальный поток управления в определенном определении функции (или не разрешен)?

Вкратце, мне трудно понять, какой контекст для использования этого действительно есть, что его использует для общения с клиентами и каковы ожидаемые результаты применения этого ключевого слова.

Ответ 1

Во-первых, у меня возникают проблемы с изображением того, что подразумевается под "такими случаями". У меня есть общее представление о том, что проблема, но не может придумать хороший пример.

Вот пример:

interface SomeInterface {
    fun someFunction(): Unit
}

inline fun someInterfaceBy(f: () -> Unit): SomeInterface { 
    return object : SomeInterface {
        override fun someFunction() = f() 
        //                            ^^^
        // Error: Can't inline 'f' here: it may contain non-local returns. 
        // Add 'crossinline' modifier to parameter declaration 'f'.
    }
}

Здесь функция, которая передается в someInterfaceBy { ... }, встроена в анонимный класс, реализующий SomeInterface. Компиляция каждого сайта-клиента someInterfaceBy создает новый класс с другой реализацией someFunction().

Чтобы узнать, что может пойти не так, рассмотрите вызов someInterfaceBy { ... }:

fun foo() {
    val i = someInterfaceBy { return }
    // do something with `i`
}

Внутри встроенной лямбды return является нелокальным и на самом деле означает возврат из foo. Но поскольку лямбда не вызывается и не протекает в объект i, возврат из foo может быть абсолютно бессмысленным: что, если i.someFunction() (и, следовательно, лямбда) вызывается после того, как foo уже возвращено или даже в другой поток?

В общем случае "такие случаи" означают функции inline, которые вызывают их функциональные параметры не в их собственных телах (эффективно, то есть принимая во внимание другие встроенные функции), но внутри некоторых других функций, которые они объявляют, например, в нестрочных лямбдах и анонимных объектов.


Во-вторых, фраза "Чтобы указать это", можно читать несколькими способами. Чтобы указать, что? Что конкретный случай не допускается? Что это разрешено? Этот нелокальный поток управления в определенном определении функции (или не разрешен)?

Именно так проблема, описанная выше, исправлена ​​в дизайне языка Котлина: всякий раз, когда функция inline намеревается встроить свой функциональный параметр где-нибудь, где его можно было бы не вызывать на месте, но хранить и вызывать позже, параметр функции inline следует пометить как crossinline, указывая на то, что в lambdas, переданном здесь, не допускается нелокальный поток управления.