[Question]: E0515 - Returns a value referencing data owned by the current function

Hello!
I'm new to Rust and I came across the issue E0515.

Here's my code below.

fn makenumber<'a>(&mut self) -> Token<'a> {
    let mut num: String = "".to_string();
    let mut dots = 0;
    let start = self.pos.copy();

    while (! self.end) && (DIGITS.to_string() + ".").contains(self.ch) {
        if self.ch == '.' {
            if dots >= 1 {break}
            dots += 1;
            num += ".";
        } else {
            num += self.ch.to_string().as_str();
        }

        self.advance();
    }

    self.retreat();
    if self.ch != '.' {
        self.advance()
    }

    if dots == 0 {
        return Token {token: TT_INT, value: num.as_str(), start: start, end: self.pos.copy()}
    } else {
        return Token {token: TT_FLOAT, value: num.as_str(), start: start, end: self.pos.copy()}
    }
}

The error was on the lines

return Token {token: TT_INT, value: num.as_str(), start: start, end: self.pos.copy()}
// and
return Token {token: TT_FLOAT, value: num.as_str(), start: start, end: self.pos.copy()}

The error said 'cannot return value referencing local variable `num`'. How can I fix this?

Here is my Token struct & implementation code:

#[derive(Clone)]
pub struct Token<'a> {
    pub token  : &'a str,
    pub value  : &'a str,
    pub start  : lexer::LexerPosition,
    pub end    : lexer::LexerPosition
}
impl fmt::Display for Token<'_> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let tokenname: ColoredString;

        if [TT_EOL, TT_EOF].contains(&self.token) {
            tokenname = self.token.truecolor(68, 73, 75);
        } else if [TT_EQUALEQ, TT_NOTEQ, TT_LSSTHN, TT_LSSTHNEQ, TT_GRTTHN, TT_GRTTHNEQ].contains(&self.token) {
            tokenname = self.token.truecolor(192, 0, 0);
        } else if [TT_INT, TT_FLOAT, TT_STRING].contains(&self.token) {
            tokenname = self.token.truecolor(0, 192, 0);
        } else if [TT_PLUS, TT_MINUS, TT_TIMES, TT_DIVBY, TT_POW, TT_SQRT, TT_CAST].contains(&self.token) {
            tokenname = self.token.truecolor(157, 128, 0);
        // Blue
        } else if [TT_PLUSEQ, TT_MINUSEQ, TT_TIMESEQ, TT_DIVBYEQ, TT_POWEQ, TT_SQRTEQ].contains(&self.token) {
            tokenname = self.token.truecolor(174, 64, 0);
        } else if [TT_EQUALS].contains(&self.token) {
            tokenname = self.token.truecolor(0, 255, 255);
        } else {
            tokenname = self.token.normal()
        }

        if self.value.len() >= 1 {
            let tokenvalue: ColoredString;
            if [TT_INT, TT_FLOAT].contains(&self.token) {
                tokenvalue = self.value.truecolor(181, 206, 168);
            } else if [TT_STRING].contains(&self.token) {
                tokenvalue = format!("`{}`", self.value.truecolor(206, 145, 145)).normal();
            } else {
                tokenvalue = self.value.normal();
            }

            write!(f, "{}", format!("<{}: {}>", tokenname, tokenvalue))
        } else {
            if self.token == TT_EOL {
                write!(f, "<{}>\n", tokenname)
            } else {
                write!(f, "<{}>", tokenname)
            }
        }
    }
}

It sounds like value should be a String rather than a &str so it can take ownership of the value. As for token, it looks like it should be an enum.

I need to implement a Copy trait for Token and String can not be copied. So I put <'a> s everywhere and changed it from String to &'a str in Token.
Also TT_INSERTHERE are all &str constants so no it is not supposed to be an enum.

pub const TT_INT      : &str = "INT";      // \d(_?\d)*
pub const TT_FLOAT    : &str = "FLOAT";    // \d(_?\d)*\.\d(_?\d)*
pub const TT_STRING   : &str = "STRING";   // ("|').*\1

pub const TT_PLUS     : &str = "PLUS";     // \+
pub const TT_MINUS    : &str = "MINUS";    // -
pub const TT_TIMES    : &str = "TIMES";    // \*
pub const TT_DIVBY    : &str = "DIVBY";    // \/
pub const TT_POW      : &str = "POW";      // \*\*
pub const TT_SQRT     : &str = "SQRT";     // \/\/

pub const TT_PLUSEQ   : &str = "PLUSEQ";   // \+=
pub const TT_MINUSEQ  : &str = "MINUSEQ";  // -=
pub const TT_TIMESEQ  : &str = "TIMESEQ";  // \*=
pub const TT_DIVBYEQ  : &str = "DIVBYEQ";  // \/=
pub const TT_POWEQ    : &str = "POWEQ";    // \*\*=
pub const TT_SQRTEQ   : &str = "SQRTEQ";   // \/\/=

pub const TT_EQUALS   : &str = "EQUALS";   // =

pub const TT_EQUALEQ  : &str = "EQUALEQ";  // ==
pub const TT_NOTEQ    : &str = "NOTEQ";    // !=
pub const TT_LSSTHN   : &str = "LSSTHN";   // <
pub const TT_LSSTHNEQ : &str = "LSSTHNEQ"; // <=
pub const TT_GRTTHN   : &str = "GRTTHN";   // >
pub const TT_CAST     : &str = "CAST";     // >>
pub const TT_GRTTHNEQ : &str = "GRTTHNEQ"; // >=

pub const TT_LPAREN   : &str = "LPAREN";   // \(
pub const TT_RPAREN   : &str = "RPAREN";   // \)

pub const TT_EOL      : &str = "EOL";      // \n
pub const TT_EOF      : &str = "EOF";      // $

It is not possible to have both of these:

  1. Destroying num before Token.
  2. Having Token be Copy.

You have to give one of them up. Maybe Clone would be ok?

As for token, it does indeed still seem like it would be better with an enum rather than string constants, but if you do want to keep the strings, you should use the &'static str type since it will always point at an immutable global, and hence have no limit on its lifetime.

Hmm. Is there any work around?

struct Parser<'a> {
    tokens: Vec<tokens::Token<'a>>,
    index: usize,
    curtoken: tokens::Token<'a>
}
impl Parser<'_> {
    fn advance(&mut self) {
        self.index += 1;
        if self.index < self.tokens.len() {
            self.curtoken = self.tokens[self.index];
//                          ^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `version::tokens::Token<'_>`, which does not implement the `Copy` trait
    tokens: Vec<tokens::Token<'a>>,
    index: usize,
    curtoken: tokens::Token<'a>
}
impl Parser<'_> {
    fn advance(&mut self) {
        self.index += 1;
        if self.index < self.tokens.len() {
            self.curtoken = self.tokens[self.index];
        }
    }
}

You can clone the token. Alternatively you could look it up in the vector every time you need it.

Clone the token when I add it to the Vec<Token>?

Whenever you get this error:

move occurs because value has type `version::tokens::Token<'_>`, which does not implement the `Copy` trait

so:

self.curtoken = self.tokens[self.index].clone();

One more thing (I think), this problem also comes up:

pub fn parse(tokens: Vec<tokens::Token>) {
    let mut parser = Parser {
        tokens: tokens,
        index: 0,
        curtoken: tokens[0]
//                ^^^^^^^^^ borrow of moved value: `tokens`
//                                 `tokens[0].clone()` does not work
    };
}

Please post the full error when using .clone().

Edit: I know why. Make a clone before moving tokens into the field. Either this:

pub fn parse(tokens: Vec<tokens::Token>) {
    let mut parser = Parser {
        curtoken: tokens[0].clone(),
        tokens: tokens,
        index: 0,
    };
}

or this:

pub fn parse(tokens: Vec<tokens::Token>) {
    let curtoken = tokens[0].clone();
    let mut parser = Parser {
        tokens: tokens,
        index: 0,
        curtoken,
    };
}
1 Like

The first one is what I had just rearranged. The second one worked, which to me makes no sense. What's the difference between:

let mut parser = Parser {
    tokens: tokens,
    index: 0,
    curtoken: tokens[0].clone(),
};

and

let curtoken = tokens[0].clone();
let mut parser = Parser {
    tokens: tokens,
    index: 0,
    curtoken: curtoken,
};

But it worked. Thank you!

Struct constructors process the fields in the order you list them. tokens: tokens moves the vector out of the variable and into the partially-built struct, so the value isn’t available anymore to make a clone of.

The problem is that once tokens is moved into the struct, you can no longer access it.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.