Как вернуть Vec структур с полем типа String из функции?

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

/// ### lex
/// Pushes the tokens generated by
/// `scan_token` to `self.tokens`
fn lex(&mut self) -> Vec<Token> {
    while !Self::is_at_eof(self) {
        self.lexeme_start = self.lookahead;
        self.tokens.push(Self::scan_token(self).unwrap());
    }
    self.tokens
        .push(Token::new(TokenType::EOF, String::from(""), self.row));
    self.tokens
}

Вектор self.tokens: Vec<Token> должен содержать токены, определенные как

pub struct Token {
    // -- snip of copyable fields --
    lexeme: String, // <-- the issue
    // -- snip of copyable fields --
}

Однако это не будет компилироваться, так как тип String не реализует трейт Copy. Как я могу вернуть этот вектор, передав право собственности вызывающей функции (например, переместить его)?

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


person SeSodesa    schedule 19.05.2020    source источник
comment
Что вы ожидаете от self.tokens после возвращения?   -  person SCappella    schedule 19.05.2020
comment
Также почему вы вызываете методы через структуру? Почему Self::is_at_eof(self) вместо self.is_at_eof()?   -  person Masklinn    schedule 19.05.2020
comment
@Masklinn Потому что я нуб на Rust (и просто программирую в целом), и я не знаю, что делаю?   -  person SeSodesa    schedule 19.05.2020
comment
Справедливо. Self::method (синтаксис унифицированного вызова функции) полезен для передачи метода функциям более высокого порядка (если сигнатуры совпадают), но обычно вы бы прямо вызывали метод как self.<method>. Помимо того, что он короче, он также попытается адаптировать self к правильному ссылочному типу.   -  person Masklinn    schedule 19.05.2020


Ответы (1)


Однако это не будет компилироваться, так как тип String не реализует трейт Copy. Как я могу вернуть этот вектор, передав право собственности вызывающей функции (например, переместить его)?

Вы... не можете? На самом деле это не имеет смысла, почему вы оба сохраняете поток токенов на себе и возвращаете его? То или другое имело бы смысл (в конце концов, вызывающая сторона могла бы просто получить токены от токенизатора, если бы захотела). Или, если вам нужна возможность, скажем, цепочек вызовов по какой-либо причине, вы можете вернуть ссылку на поток токенов, принадлежащий тому, кем является Self.

/// Option 0: return a reference to the Vec (could be mutable, so you could push into it)
fn lex0(&mut self) -> &Vec<Token> {
    while !self.is_at_eof() {
        self.lexeme_start = self.lookahead;
        self.scan_token();
    }
    self.tokens.push(Token::new(TokenType::EOF, String::from(""), self.row));
    &self.tokens
}
/// Option 1: return a slice reference (could be mutable, couldn't push into it)
fn lex1(&mut self) -> &[Token] {
    while !self.is_at_eof() {
        self.lexeme_start = self.lookahead;
        self.scan_token();
    }
    self.tokens.push(Token::new(TokenType::EOF, String::from(""), self.row));
    &self.tokens
}

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

/// Option 2: consume lexer and return tokens stream
fn lex2(mut self) -> Vec<Token> {
    while !self.is_at_eof() {
        self.lexeme_start = self.lookahead;
        self.scan_token();
    }
    self.tokens.push(Token::new(TokenType::EOF, String::from(""), self.row));
    self.tokens
}

Наконец, вы можете реализовать Clone на Token и клонировать весь Vec, чтобы вернуть его, но это кажется неэффективным.

#[derive(Clone)]
struct Token {...}

/// Option 3: copy tokens stream
fn lex3(&mut self) -> Vec<Token> {
    while !self.is_at_eof() {
        self.lexeme_start = self.lookahead;
        self.scan_token();
    }
    self.tokens.push(Token::new(TokenType::EOF, String::from(""), self.row));
    self.tokens.clone()
}

Не зная на самом деле, в чем заключается основная потребность, трудно дать хорошие рекомендации.

person Masklinn    schedule 19.05.2020
comment
Я хочу иметь возможность передать последовательность (вектор) токенов, сгенерированных лексером, парсеру, а затем избавиться от лексера. В этом случае использование лексера действительно не проблема. - person SeSodesa; 19.05.2020
comment
Добавил несколько примеров. В этом случае будет работать версия lex2: если функция просто возьмет себя по значению (а не по ссылке) и вернет self.tokens, как вы делаете сейчас, она переместит вектор из лексера и сожжет указанный лексер. - person Masklinn; 19.05.2020