0

I'm using trait objects to abstract over the database engine. I have Database and Snapshot traits that can be implemented for different database engines, but I can't figure out how to return the Snapshot trait object without using unsafe.

use std::marker::PhantomData;
use std::mem;
use std::sync::Arc;

pub trait Database: Send + Sync + 'static {
    fn snapshot(&self) -> Box<dyn Snapshot>;
}

trait Snapshot: 'static {
    fn foo(&self);
}

struct DB {}

struct DBSnapshot<'a> {
    _lifetime: PhantomData<&'a ()>,
}

impl DB {
    fn snapshot(&self) -> DBSnapshot {
        DBSnapshot {
            _lifetime: PhantomData,
        }
    }
}

struct DBWrapper {
    db: Arc<DB>,
}

impl Database for DBWrapper {
    fn snapshot(&self) -> Box<dyn Snapshot> {
        Box::new(DBWrapperSnapshot {
            snapshot: self.db.snapshot(),
            // Will work with `transmute`
            // snapshot: unsafe { mem::transmute(self.db.snapshot()) },
            db: Arc::clone(&self.db),
        })
    }
}

struct DBWrapperSnapshot {
    snapshot: DBSnapshot<'static>,
    db: Arc<DB>,
}

impl Snapshot for DBWrapperSnapshot {
    fn foo(&self) {}
}

Without unsafe I get the error:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/main.rs:32:35
   |
32 |                 snapshot: self.db.snapshot(),
   |                                   ^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 30:9...
  --> src/main.rs:30:9
   |
30 | /         fn snapshot(&self) -> Box<dyn Snapshot> {
31 | |             Box::new(DBWrapperSnapshot {
32 | |                 snapshot: self.db.snapshot(),
33 | |                 // Will work with `transmute`
...  |
36 | |             })
37 | |         }
   | |_________^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:32:27
   |
32 |                 snapshot: self.db.snapshot(),
   |                           ^^^^^^^
   = note: but, the lifetime must be valid for the static lifetime...
   = note: ...so that the expression is assignable:
           expected DBSnapshot<'static>
              found DBSnapshot<'_>

The error is perfectly reasonable, but is it possible to avoid using transmute here? Because I want to be sure that snapshot field in DBWrapperSnapshot will not outlive reference to self.db. Link to playground

Pavel M.
  • 263
  • 1
  • 9
  • 3
    This use of `transmute` is not sound. `DBSnapshot` depends on a lifetime `'a` but you are unsafely coercing it to the `'static` lifetime. – Peter Hall Jul 05 '19 at 14:03
  • 1
    _"is it possible to avoid using transmute here?"_ — I think you need to expand your question to explain what you are **really** trying to accomplish. As is, no you cannot safely treat an arbitrary lifetime as `'static`. – Peter Hall Jul 05 '19 at 14:20
  • I believe your question might be answered by the answers of [The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want](https://stackoverflow.com/q/40053550/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Jul 05 '19 at 15:17
  • Possible duplicate of [The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want](https://stackoverflow.com/questions/40053550/the-compiler-suggests-i-add-a-static-lifetime-because-the-parameter-type-may-no) – E_net4 Jul 05 '19 at 17:25
  • @PeterHall I want to return snapshot with reference to database and be sure that snapshot will not outlive database reference. – Pavel M. Jul 05 '19 at 19:34
  • @Shepmaster I've edited my question and provide link to playgroung. – Pavel M. Jul 05 '19 at 19:35
  • 1
    *with reference to database* — you cannot, as you have declared that the trait cannot have any references other than `'static`: `trait Snapshot: 'static`. We don't know **why** you have done this, but your code, as written, is impossible. As mentioned, your `unsafe` breaks the rules and you are liable to encounter memory safety bugs; you must not do this. – Shepmaster Jul 05 '19 at 20:09

0 Answers0