Varying fields based on a generic parameter, `?Sized` and bizarre errors

The goal of the following rather lengthy chunk of sample code is to define a type ScopedCell<T> that you use as a static variable, you set from a reference to a local variable near the top of your call chain, and you can then get from deep within the call chain without having to pass the reference all the way down. Yes, this is not entirely a safe thing to do. Please leave that aside for now, it's not relevant to the problem.

The problem is that I need two concrete types to be T: Mutex<LineWriter<File>>> and str. And I tried to be clever about this, because it occurred to me that when T is Sized, ScopedCell<T> could be implemented using AtomicPtr instead of another Mutex. But, as you all probably know better than me already, it ain't easy to vary the type of a field of a generic struct based on a generic parameter.

This is as far as I got:

`ScopedCell` implementation and sample uses (long)
use std::marker::PhantomData;
use std::sync::Mutex;
use std::sync::atomic::{AtomicPtr, Ordering};
use std::mem::transmute;
use std::ptr;

use std::fs::File;
use std::io::LineWriter;

static PROGNAME: ScopedCell<str> = ScopedCell::new();

pub fn set_progname(progname: &str) -> ScopedCellGuard<str> {
    PROGNAME.set(progname)
}

pub unsafe fn get_progname() -> Option<&'static str> {
    PROGNAME.get()
}

static WRITER: ScopedCell<Mutex<LineWriter<File>>> = ScopedCell::new();

pub fn set_writer(progname: &Mutex<LineWriter<File>>) -> ScopedCellGuard<Mutex<LineWriter<File>>> {
    WRITER.set(progname)
}

pub unsafe fn writer() -> Option<&'static Mutex<LineWriter<File>>> {
    WRITER.get()
}


/// Helper trait for ScopedCell which enables it to hold both
/// sized and (some) unsized types.  Sized types can use AtomicPtr,
/// unsized types require a mutex.
trait ScopedCellTypes {
    type Holder;
    type Cell;
}

impl<T> ScopedCellTypes for T where T: Sized {
    type Holder = AtomicPtr<T>;
    type Cell = ScopedCell<T>;
}

impl ScopedCellTypes for str {
    type Holder = Mutex<Option<&'static str>>;
    type Cell = ScopedCell<str>;
}

/// static THING: ScopedCell<T> holds a nullable reference to T which
/// is Some within a particular dynamic scope.
pub(crate) struct ScopedCell<T: ScopedCellTypes + ?Sized> {
    holder: <T as ScopedCellTypes>::Holder,
}

impl<T: Sized> ScopedCell<T> {
    /// Create a new empty ScopedCell.
    pub const fn new() -> Self {
        Self { holder: AtomicPtr::new(ptr::null_mut()) }
    }

    /// Get the current value of the ScopedCell.
    /// The actual lifetime of this value is not available, so caller
    /// is responsible for ensuring that it does not hold onto the
    /// reference longer than it actually lives.
    unsafe fn get(&self) -> Option<&'static T> {
        // SAFETY: holder is either null or was initialized from a valid
        // reference by `set`.
        unsafe { self.holder.load(Ordering::Acquire).cast_const().as_ref() }
    }

    /// Store `val` in self.  Returns a guard object which will clear
    /// self again when it is dropped, with lifetime bounded by the
    /// lifetime of `val`, ensuring that `get` will never return a
    /// dangling reference.
    fn set<'a, 'b: 'a>(&'b self, val: &'a T) -> ScopedCellGuard<'a, T> {
        // SAFETY: `val` is a valid reference and the guard is
        // responsible for erasing our copy at the right time.
        // The cast_mut here is paired with a cast_const in get();
        // a mutable reference is never exposed.
        self.holder.store((val as *const T).cast_mut(), Ordering::Release);
        ScopedCellGuard::<T>::new(self)
    }

    /// Erase the value stored in self, if any.  Normally not necessary
    /// to call explicitly.
    fn clear(&self) {
        self.holder.store(ptr::null_mut(), Ordering::Release);
    }
}

impl ScopedCell<str> {
    pub const fn new() -> Self {
        Self { holder: Mutex::new(None) }
    }

    unsafe fn get(&self) -> Option<&'static str> {
        *self.holder.lock().unwrap()
    }
    fn set<'a, 'b: 'a>(&'b self, val: &'a str) -> ScopedCellGuard<'a, str> {
        *self.holder.lock().unwrap() = Some(unsafe { transmute(val) });
        ScopedCellGuard::<str>::new(self)
    }
    fn clear(&self) {
        *self.holder.lock().unwrap() = None;
    }
}

pub(crate) struct ScopedCellGuard<'a, T: ScopedCellTypes + ?Sized> {
    cell: &'a <T as ScopedCellTypes>::Cell,
    lifetime: PhantomData<&'a T>,
}

impl<'a, T: ScopedCellTypes + ?Sized> ScopedCellGuard<'a, T> {
    fn new(cell: &'a <T as ScopedCellTypes>::Cell) -> Self {
        Self { cell, lifetime: PhantomData }
    }
}

impl<'a, T: ScopedCellTypes + ?Sized> Drop for ScopedCellGuard<'a, T> {
    fn drop(&mut self) {
        self.cell.clear()
    }
}

(playground link)

And here's the errors I'm getting:

error[E0034]: multiple applicable items in scope
  --> src/lib.rs:10:48
   |
10 | static PROGNAME: ScopedCell<str> = ScopedCell::new();
   |                                                ^^^ multiple `new` found
   |
note: candidate #1 is defined in an impl for the type `ScopedCell<T>`
  --> src/lib.rs:57:5
   |
57 |     pub const fn new() -> Self {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
note: candidate #2 is defined in an impl for the type `ScopedCell<str>`
  --> src/lib.rs:92:5
   |
92 |     pub const fn new() -> Self {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0034]: multiple applicable items in scope
  --> src/lib.rs:20:59
   |
20 | static WRITER: ScopedCell<LineWriter<File>> = ScopedCell::new();
   |                                                           ^^^ multiple `new` found
   |
note: candidate #1 is defined in an impl for the type `ScopedCell<T>`
  --> src/lib.rs:57:5
   |
57 |     pub const fn new() -> Self {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
note: candidate #2 is defined in an impl for the type `ScopedCell<str>`
  --> src/lib.rs:92:5
   |
92 |     pub const fn new() -> Self {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0599]: no method named `clear` found for reference `&'a <T as ScopedCellTypes>::Cell` in the current scope
   --> src/lib.rs:121:19
    |
121 |         self.cell.clear()
    |                   ^^^^^ method not found in `&<T as ScopedCellTypes>::Cell`

The E0034 errors are annoying -- I spelled out the exact type I wanted on the left side of the assignment, that should be enough information to pick the correct impl -- but easily surmountable (by using the turbofish on the right side). The E0599 error, on the other hand, has stopped me completely. No matter what T is, as long as it satisfies the ScopedCellTypes + ?Sized bound, the concrete type <T as ScopedCellTypes>::Cell is one or the other variant of ScopedCell<T> and they both have a clear method, so there should be no problem. I tried moving get, set and clear to a trait but that helped not at all (it actually made the errors quite a bit messier).

(I don't even really understand why the indirection through ScopedCellTypes::<T>::Cell is necessary in the first place. I originally had cell: &ScopedCell<T> but then the compiler coughed up a mess of errors about T not necessarily being Sized, which is true but does not appear to be relevant given that both ScopedCell and ScopedCellGuard are impl'd very specifically for the same ScopedCellTypes + ?Sized bound...)

Any help would be most appreciated.

Drop implementations can't have extra constraints (like a fixed set of types not expressed in the definition of the struct); they have to have the same constraints as the struct. As it happens, you do already have some constraints:

//       vvvvvvvvvvvvvvvvvv
impl<'a, T: ScopedCellTypes + ?Sized> Drop for ScopedCellGuard<'a, T> {

So you can work the functionality into those constraints without much change:

trait ScopedCellTypes {
    type Holder;
    type Cell: Clear; // <-- new bound
}

// New trait
trait Clear {
    fn clear(&self);
}

impl<T> Clear for ScopedCell<T> { /* move the method you have to here */ }
impl Clear for ScopedCell<str> { /* move the method you have to here */ }

(You could move all the functionality into the trait, but I just kept my changes minimal.)

Thanks! Your solution is very similar to something I tried myself; the key thing I didn't realize I needed is to put a :Clear bound on the cell type.

I wonder if you can explain why the turbofishes are necessary in the static ScopedCell definitions.

static PROGNAME: ScopedCell<str> = ScopedCell::<str>::new();
static WRITER: ScopedCell<LineWriter<File>> = ScopedCell::<LineWriter<_>>::new();

Since the exact type of each item is spelled out on the left side of each assignment, it seems like one should be able to write simply ScopedCell::new() on the right in both cases. And the fact that you can turn File into a placeholder on the right, but not the whole type argument, makes it make even less sense to me.

I can't explain why inference gives up before looking at the return types in that case.