Can't get rid of a "lifetime-dependent variable 'self' escapes its scope" error

I thought I was getting pretty savvy with ~Escapable types, but today I’ve run into a compiler error that doesn’t make sense and I cannot find a workaround for.

import SQLite3

struct SQLBlob: ~Escapable {
    @_lifetime(borrow val)
    init(_ val: SQLFnValue) {
        self.bytes = RawSpan(_unsafeStart: sqlite3_value_blob(val._val),
                             byteCount: Int(sqlite3_value_bytes(val._val)))
    }

    let bytes: RawSpan

    var data: Data {Data(rawSpan: bytes)}
}

struct SQLFnValue {
    fileprivate let _val: OpaquePointer?
    // (rest of implementation omitted)
}

In Swift 6.3 (Xcode 26.4) this invariably barfs compiling the init method:

SQLiteFns.swift:240:5: error: lifetime-dependent variable 'self' escapes its scope
    init(_ val: SQLFnValue) {
    ^
SQLiteFns.swift:240:5: note: error in compiler-generated 'init'
    init(_ val: SQLFnValue) {
    ^
SQLiteFns.swift:241:44: note: it depends on the lifetime of this parent value
        self.bytes = RawSpan(_unsafeStart: sqlite3_value_blob(val._val),
                                           ^
SQLiteFns.swift:243:5: note: this use causes the lifetime-dependent value to escape
    }

  1. How can self escape its scope in a struct declaration that has no consuming methods?
  2. What’s the “compiler-generated ‘init’” it’s talking about? The hints point to an explicit init; why would there be an implicit one?
  3. How does anything depend on the lifetime of the result of sqlite3_value_blob()? That’s an external C function that returns an UnsafeRawPointer. Unsafe pointers don’t have a lifetime, and <sqlite3.h> has no Swift-specific annotations.

Since you're using the init(_unsafeStart:byteCount:) initializer, the value will depend on the scope of the UnsafeRawPointer variable you passed in, which is generally conservative and not what you really want. You'll need to use _overrideLifetime to give the RawSpan the actual lifetime you want it to. You can look at the implementation of, for instance, the span property on Array for an example of how to do this.

self is the newly-initialized value, which is implicitly returned from the init. The escape occurs because of that implicit return, because the default lifetime of the value containing the RawSpan is too short.

That looks like a bug, since it's referring to the explicit init.

1 Like

Thanks! I wasn’t aware of the _overrideLifetime function; that’s good to know. With some help from autocomplete I came up with this change to the init', which compiles successfully; is this the right way to use it?

    @_lifetime(borrow val)
    init(_ val: SQLFnValue) {
        let span = RawSpan(_unsafeStart: sqlite3_value_blob(val._val),
                           byteCount: Int(sqlite3_value_bytes(val._val)))
        self.bytes = _overrideLifetime(span, borrowing: val)
    }
1 Like

That looks good, yeah.