Difference between named type and Self

Hello! I'm doing Scanning · Crafting Interpreters but with Rust :smiley:

While implementing the Scanner type, I got into this problem

pub struct Scanner<'a> {
    chars: Peekable<Chars<'a>>,
    tokens: Vec<Token>,
    errors: Vec<usize>,
    line: usize,
}

impl<'a> Scanner<'a> {
    pub fn new(source: &str) -> Scanner {
        Scanner {
            chars: source.chars().peekable(),
            tokens: vec![],
            errors: vec![],
            line: 1,
        }
    }

This does compile, but if I try this

pub struct Scanner<'a> {
    chars: Peekable<Chars<'a>>,
    tokens: Vec<Token>,
    errors: Vec<usize>,
    line: usize,
}

impl<'a> Scanner<'a> {
    pub fn new(source: &str) -> Self {
        Scanner {
            chars: source.chars().peekable(),
            tokens: vec![],
            errors: vec![],
            line: 1,
        }
    }

It does not. Can someone explain the difference to me? I thought that Self and the name of the type were interchangeable.

What does the error message say?

on the second case

error[E0621]: explicit lifetime required in the type of source

  --> src/lox/scanner.rs:33:9
   |
32 |       pub fn new(source: &str) -> Self {
   |                          ---- help: add explicit lifetime `'a` to the type of `source`: `&'a str`
33 | /         Scanner {
34 | |             chars: source.chars().peekable(),
35 | |             tokens: vec![],
36 | |             errors: vec![],
37 | |             line: 1,
38 | |         }
   | |_________^ lifetime `'a` required

Hi, it's because in the first case, the compiler will infer the lifetime:

fn new<'s>(source: &'s str) -> Scanner<'s>

But Self already has a lifetime named 'a. The error message suggests you explicit the lifetime of source.

fn new(source: &'a str) -> Self
1 Like

Ooh, that makes sense! Self is subtituing for Scanner<'a> here, so I need to tell the compiler that source has that same lifetime. While with Scanner, he infers that!

Thanks @leudz!

1 Like

Another way to solve this would be to follow the compiler's instructions and bind the input string to 'a

impl<'a> Scanner<'a> {
    pub fn new(source: &'a str) -> Self {
        Self {
            chars: source.chars().peekable(),
            tokens: vec![],
            errors: vec![],
            line: 1,
        }
    }
}
3 Likes

This is what I ended up going with, but I didn't understand the need to do so untill now :smile:

1 Like

You can add the #![deny(elided_lifetimes_in_paths)] lint to your project to better catch the hidden lifetime in Scanner-like structs :wink:

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.