Код выполняется в основном режиме, но завершается с ошибкой: «нельзя заимствовать как изменяемый более одного раза за раз» при извлечении в функцию

Я хотел бы выполнить небольшой фрагмент JavaScript, используя ящик deno_core, получить объект результата, получить свойство test и выполнить его как функцию.

Следующий код работает, когда все написано в main, но не работает, когда я пытаюсь извлечь его как функцию.

Как избежать подобных ситуаций/почему это работает в main/как правильно это извлечь (если можно это извлечь как одну функцию)?

use deno_core::v8;
use deno_core::JsRuntime;

fn main() -> anyhow::Result<()> {
    let mut runtime = JsRuntime::new(Default::default());
    let executed = runtime.execute_script("name", "({ test: (a, b) => { console.info(a + b) } })")?;
    let scope = &mut runtime.handle_scope();
    let local_executed = v8::Local::new(scope, executed);
    let result_obj = v8::Local::<v8::Object>::try_from(local_executed)?;
    let param1 = v8::String::new(scope, "Hello").unwrap();
    let param2 = v8::String::new(scope, "World").unwrap();
    let args = vec![param1.into(), param2.into()];

    let fn_name: v8::Local<v8::String> = v8::String::new(scope, "my_function").unwrap();
    result_obj.get(scope, fn_name.into())
        .map(|value| v8::Local::<v8::Function>::try_from(value))
        .unwrap()?
        .call(scope, local_executed, &args);

    // If I call get_fn and call the result v8::Function here like this, it fails to compile:
    //
    // get_fn(scope, result_obj).call(scope, local_executed, &args);
    //        -----              ---- ^^^^^ second mutable borrow occurs here
    //        |                  |
    //        |                  first borrow later used by call
    //        first mutable borrow occurs here
    //
    // error[E0499]: cannot borrow `*scope` as mutable more than once at a time

    Ok(())
}

fn get_fn<'a>(scope: &'a mut v8::HandleScope, object: v8::Local<'a, v8::Object>) -> v8::Local<'a, v8::Function> {
    let fn_name = v8::String::new(scope, "test").unwrap();
    object.get(scope, fn_name.into())
        .map(|value| v8::Local::<v8::Function>::try_from(value))
        .unwrap()
        .unwrap()
}

Это означает, что вы неправильно указываете время жизни.

Chayim Friedman 01.09.2024 23:04

@ChayimFriedman Там сказано, что время жизни возвращаемого значения должно быть таким же, как время жизни параметра scope (время жизни второго параметра, похоже, не имеет значения, я пробовал его изменить).

almafa 02.09.2024 00:53
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
2
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Решение — изменить &'a mut v8::HandleScope на &mut v8::HandleScope<'a> в сигнатуре функции.

Вот мое слишком подробное объяснение:

Во-первых, v8::HandleScope — это тип с параметром времени жизни. Итак, можно писать v8::HandleScope<'s>. Написание просто v8::HandleScope называется исключением параметра времени жизни, и это просто сообщает компилятору, что мы не собираемся его использовать, и позволяет избежать необходимости давать ему имя только для того, чтобы никогда его не использовать.

Итак, когда мы определяем сигнатуру функции, принимающую значение типа &'a mut v8::HandleScope, мы ограничиваем 'a временем жизни этой ссылки на область, а затем исключаем время жизни самой области. Теперь все остальные функции, которым нужен &mut HandleScope, например v8::String::new и v8::Local::get, имеют параметр времени жизни 's и они ищут &mut v8::HandleScope<'s>. И затем значения, которые они возвращают, будут 's не выходить за рамки той области, к которой они принадлежат. Это означает, что время жизни v8::Local, возвращаемого этой функцией, на самом деле является временем жизни HandleScope, которое мы игнорируем. С этого момента я буду называть эту пропущенную жизнь 's. Итак, мы можем задаться вопросом, как вообще эта функция компилируется, если мы сказали, что собираемся вернуть Local<'a>, но на самом деле мы возвращаем Local<'s>? Причина, по которой это разрешено, заключается в том, что компилятор довольно умен и знает, что, поскольку мы удерживаем &'a перед HandleScope, то этот HandleScope<'s> должен жить дольше, чем &'a перед ним, потому что ссылки не могут пережить объект, на который они указывают, следовательно, это доказывает, что 's длиннее, чем 'a, то есть, если объект действителен для 's, то он действителен и для 'a. Таким образом, поскольку требование к Local<'a> заключается в том, что это Local, действительное в течение всей жизни 'a, наш Local<'s>, действительный для 's, может привести к Local<'a>, потому что у нас есть гарантия, что он действителен для 'a. Но, конечно, нам не очень хотелось таким образом ограничивать время жизни этого Local. Мы непреднамеренно связали время жизни Local со временем существования нашей заимствованной области, что означает, что средство проверки заимствований по-прежнему будет считать область заимствованной даже после возврата функции. По сути, мы сказали, что этот локальный объект действителен только до тех пор, пока ссылка на область видимости остается неизменной, но на самом деле это не так. Это правда, что локальный адрес наверняка действителен, пока существует ссылка, но он также действителен и после этого, особенно до тех пор, пока область видимости не будет удалена. Это вся жизнь 's. Итак, все, что нам нужно сделать, это изменить сигнатуру функции на:

fn get_fn<'s>(scope: &mut v8::HandleScope<'s>, object: v8::Local<'s, v8::Object>) -> v8::Local<'s, v8::Function>

И тогда мы не будем предъявлять какие-то странные и слишком жесткие требования к срокам жизни, и вы сможете успешно вызывать get_fn(scope, result_obj).call(scope, local_executed, &args) в main.

Небольшая придирка: «... это называется исключением параметра времени жизни, и это просто сообщает компилятору, что мы не собираемся его использовать, и позволяет избежать необходимости давать ему имя только для того, чтобы никогда его не использовать». Разве исключение времени жизни не использует просто время жизни аргументов? fn fun(foo: &Foo) -> &Bar то же самое, что fn fun<'a>(foo: &'a Foo) -> &'a Bar, и будет жаловаться, если у вас нет заимствованного аргумента, например, fn fun(foo: Foo) -> &Bar требует явного времени жизни и его нельзя исключить

MeetTitan 02.09.2024 19:47

В противном случае высококачественный ответ заслуживает похвалы

MeetTitan 02.09.2024 19:53

Другие вопросы по теме