Self type in trait implementation is not actually "self", instead is a type that only implements the traits the trait specifies

I am unsure why this code does not compile, since when creating a FileSystemLocation, it takes an Arc<dyn FileSystem>, and then when I create a FileSystemLocation in the FileSystem trait implementation, self should be dyn FileSystem, but the trait error claims that self does not implement FileSystem. It's really weird, since of course self implements FileSystem, it's literally in the trait definition of that exact trait. I am unsure of what to do, and this is pretty crucial logic to my app, so any help whatsoever would be greatly appreciated.

use std::sync::Arc;

struct FileSystemLocation {
    file_system: Arc<dyn FileSystem>
}

impl FileSystemLocation {
    pub fn new(file_system: Arc<dyn FileSystem>) -> Self {
        Self {
            file_system
        }
    }
}

trait FileSystem {
    fn location_with_sub_path(&self) -> FileSystemLocation {
        FileSystemLocation::new(
            Arc::new(self.clone()),
        )
    }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `&Self: FileSystem` is not satisfied
  --> src/lib.rs:18:13
   |
18 |             Arc::new(self.clone()),
   |             ^^^^^^^^^^^^^^^^^^^^^^ the trait `FileSystem` is not implemented for `&Self`
   |
   = note: required for the cast from `Arc<&Self>` to `Arc<(dyn FileSystem + 'static)>`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` (lib) due to previous error

So, this works:

trait FileSystem {
    fn location_with_sub_path(&self) -> FileSystemLocation
    where Self: 'static + Clone {
        FileSystemLocation::new(
            Arc::new(self.clone()),
        )
    }
}

The first problem you have is that you call Clone::clone on &Self. Now, the compiler can't prove that Self will implement Clone, but references do. So what it's actually cloning is the reference to Self, not Self itself. You can see this if you read the error carefully. So we have to introduce a constraint that this method only exists if Clone is implemented.

The second issue is that the compiler can't prove that the Self type lives long enough. This is what the 'static bound fixes.

1 Like

The error message clearly points out that the type in question is &Self, i.e., a reference to self. That's becase self.clone() clones the reference itself, since Self is not itself Clone. There's no such bound on it, and there can't (usefully) be one, because Clone makes the trait non-object-safe. @DanielKeep's partial solution above has the same limitation.

With self.clone() you are actually calling &Self's clone, since Self does not require Clone. Then if you look closer to the error message, it's claiming &Self (instead of Self) not implementing FileSystem, which is correct.

Now, you might want to either:

  1. Scrap that default implementation.
  2. Using Clone as a bound. (really bad idea)
  3. Using self: Arc<Self> as trait method reciever. (A bit leaky abstraction, but might suit your use case.)
  4. Move the method out of trait definition as a free function (or extension trait). This is the best solution if you never intend to override it's implementation.
1 Like

Your default body makes me think this is a similar situation to wanting Clone for Box<dyn Trait>. If you want the method available for dyn Trait itself, you can break it out into a supertrait.

trait FsLoc {
    fn location_with_sub_path(&self) -> FileSystemLocation;
}

// implicitly: + Sized
// (The compiler will provide the implementation for `dyn FileSystem`)
impl<T: FileSystem + Clone + 'static> FsLoc for T {
    fn location_with_sub_path(&self) -> FileSystemLocation {
        FileSystemLocation::new(
            Arc::new(self.clone()),
        )
    }
}

trait FileSystem: FsLoc {
}
1 Like