Невозможно сослаться на фрагмент & str, потому что он не живет достаточно долго, даже несмотря на то, что он ссылается - PullRequest
0 голосов
/ 27 ноября 2018

Я внедряю сканер в Rust.У меня есть метод scan в структуре Scanner, который принимает фрагмент строки в качестве исходного кода, разбивает эту строку на Vec<&str> символов UTF-8 (используя ящик unicode_segmentation), а затем делегирует каждый символ методу scan_token, который определяет его лексический токен и возвращает его.

extern crate unicode_segmentation;
use unicode_segmentation::UnicodeSegmentation;

struct Scanner {
    start: usize,
    current: usize,
}

#[derive(Debug)]
struct Token<'src> {
    lexeme: &'src [&'src str],
}

impl Scanner {
    pub fn scan<'src>(&mut self, source: &'src str) -> Vec<Token<'src>> {
        let mut offset = 0;
        let mut tokens = Vec::new();
        // break up the code into UTF8 graphemes
        let chars: Vec<&str> = source.graphemes(true).collect();
        while let Some(_) = chars.get(offset) {
            // determine which token this grapheme represents
            let token = self.scan_token(&chars);
            // push it to the tokens array
            tokens.push(token);
            offset += 1;
        }
        tokens
    }

    pub fn scan_token<'src>(&mut self, chars: &'src [&'src str]) -> Token<'src> {
        // get this lexeme as some slice of the slice of chars
        let lexeme = &chars[self.start..self.current];
        let token = Token { lexeme };
        token
    }
}

fn main() {
    let mut scanner = Scanner {
        start: 0,
        current: 0,
    };
    let tokens = scanner.scan("abcd");
    println!("{:?}", tokens);
}

Я получаю ошибку:

error[E0597]: `chars` does not live long enough
  --> src/main.rs:22:42
   |
22 |             let token = self.scan_token(&chars);
   |                                          ^^^^^ borrowed value does not live long enough
...
28 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'src as defined on the method body at 15:17...
  --> src/main.rs:15:17
   |
15 |     pub fn scan<'src>(&mut self, source: &'src str) -> Vec<Token<'src>> {
   |                 ^^^^

Полагаю, я понимаюЛогика, объясняющая, почему это не работает: ошибка проясняет, что chars должен жить столько же времени, сколько время жизни 'src, потому что tokens содержит ссылки на фрагменты в данных внутри chars.

Что я не понимаю, так это то, что chars - это просто фрагмент ссылок на объект, у которого имеет время жизни 'src (а именно source), почемуtokens не может ссылаться на эти данные после удаления chars?Я довольно новичок в низкоуровневом программировании, и я полагаю, что моя интуиция относительно ссылок + время жизни может быть несколько нарушена.

1 Ответ

0 голосов
/ 28 ноября 2018

Ваша проблема может быть уменьшена до этого:

pub fn scan<'a>(source: &'a str) -> Option<&'a str> {
    let chars: Vec<&str> = source.split("").collect();
    scan_token(&chars)
}

pub fn scan_token<'a>(chars: &'a [&'a str]) -> Option<&'a str> {
    chars.last().cloned()
}
error[E0597]: `chars` does not live long enough
 --> src/lib.rs:3:17
  |
3 |     scan_token(&chars)
  |                 ^^^^^ borrowed value does not live long enough
4 | }
  | - borrowed value only lives until here
  |
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 1:13...
 --> src/lib.rs:1:13
  |
1 | pub fn scan<'a>(source: &'a str) -> Option<&'a str> {
  |             ^^

Функция scan_token требует, чтобы ссылка на срез и ссылки внутри среза имели одинаковые время жизни: &'a [&'a str].Поскольку Vec живет в течение более короткого периода времени, это и должно быть единой жизнью.Однако время жизни вектора недостаточно велико для возврата значения.

Удалите ненужное время жизни:

pub fn scan_token<'a>(chars: &[&'a str]) -> Option<&'a str>

Применяя эти изменения к вашему полному коду, вы видитеосновная проблема повторяется в определении Token:

struct Token<'src> {
    lexeme: &'src [&'src str],
}

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

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

impl Scanner {
    pub fn scan<'src>(&mut self, source: &'src str, chars: &'src mut Vec<&'src str>) -> Vec<Token<'src>> {
        // ...
        chars.extend(source.graphemes(true));
        // ...
        while let Some(_) = chars.get(offset) {
            // ...
            let token = self.scan_token(chars);
            // ...
        }
        // ...
    }

    // ...    
}

fn main() {
    // ...
    let mut chars = Vec::new();
    let tokens = scanner.scan("abcd", &mut chars);
    // ...
}

Вы, вероятно, просто хотите, чтобы Token было Vec<&'src str>

См. также:

...