There is a footgun on PhantomData

#![allow(unused)]
use core::marker::PhantomData;
fn main() {
    let mut n = [0; 2];
    
    let mut c = C(&mut n, PhantomData); // bad: fail to capture borrow check error
    // let mut c = C::new(&mut n); // good
    
    let val = c.f();

    n = [3, 4]; // a borrow error should be raised here

    val;
}

struct C<'a, T>(*mut [T], PhantomData<&'a mut [T]>);
impl<T> C<'_, T> {
    fn new(a: &mut [T]) -> C<'_, T> { C(a, PhantomData) }
    fn f(&mut self) -> W<'_, T> {
        W(self)
    }
}

struct W<'a, T>(&'a C<'a, T>);

Edit: it seems like a problem related to PhantomData, because borrow checker works as expected without it Rust Playground

Edit2: yeah, the lifetime parameter in PhantomData is inferred to be 'static on plain type constructor, but limited to input lifetime on new function

let mut c = C(&mut n, PhantomData);
// is the same as
let mut c: C<'static, _> = C(&mut n, PhantomData); // bad: fail to capture borrow check error
2 Likes

You could call this a footgun, but there is nothing the compiler can do here to infer the intended relationship between the fields. In this situation, you should make the fields private and have an fn new().

8 Likes

It is good to know about this, thank you!

2 Likes

It may be possible to implement a lint for this — warn when a type constructor argument has a non-static lifetimes that is being cast away, and the lifetime is not part of the type being constructed.

4 Likes

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.