Cannot infer an appropriate lifetime

for autoref due to conflicting requirements

собственно код: https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=2cac758c957e574d1560544cf337496b

не пойму где ошибся.

А в трейте RawHoldingRegisters точно лайфтайм нужен? Если его убрать все будет работать.

Дело в том что при реализации, RawData будет мутабельным заимствованием RawHoldingRegister, а поскольку RawHoldingRegister является ассоциативным типом, то время жизни нужно задекларировать

Можно конечно добавить вж в HoldingRegisters:

pub trait HoldingRegisters<'a> {
    fn get<A: Into<u16>>(&'a mut self, addr: A) -> Result<u16>;

Из примера необходимость такого засорения лайфтаймами не очевидна.

Еще можно можно поменять методы в HoldingRegisters так чтобы они принимали self вместо &mut self и делать имплементацию для &'a mut T.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=4955661ce88897b3e2c4cfcb292d1129

Сделал такой вариант

pub trait HoldingRegisters {
    fn get<A: Into<u16>>(&mut self, addr: A) -> Result<u16>;
    fn gets<A: Into<u16>, D: From<u16>>(&mut self, start_addr: A, buff: &mut [D]) -> Result<()>;
    fn set<A: Into<u16>, D: Into<u16>>(&mut self, addr: A, data: D) -> Result<()>;
    fn sets<A: Into<u16>, D: Into<u16> + Copy>(&mut self, start_addr: A, buff: &[D]) -> Result<()>;
}

pub trait RawHoldingRegisters<'a> {
    type Data: RawData + 'a;
    fn put<A: Into<u16>>(&mut self, start_addr: A, registers: u16, bytes: u8) -> Self::Data;
    fn pop<A: Into<u16>>(&mut self, start_addr: A, registers: u16) -> Self::Data;
}

pub trait RawData {
    fn query(&mut self) -> Result<&mut Self>;
    fn as_slice(&self) -> &[u8];
    fn as_mut_slice(&mut self) -> &mut [u8];
}

impl<'a, T> HoldingRegisters for T where T: RawHoldingRegisters<'a> {
    fn get<A: Into<u16>>(&mut self, addr: A) -> Result<u16> {
        Ok(BigEndian::read_u16(self.pop(addr, 1).query()?.as_slice()))
    }

    fn gets<A: Into<u16>, D: From<u16>>(&mut self, _start_addr: A, _buff: &mut [D]) -> Result<()> {
        Ok(())
    }

    fn set<A: Into<u16>, D: Into<u16>>(&mut self, _addr: A, _data: D) -> Result<()> {
        Ok(())
    }

    fn sets<A: Into<u16>, D: Into<u16> + Copy>(&mut self, _start_addr: A, _buff: &[D]) -> Result<()> {
        Ok(())
    }
}

но теперь проблема с реализацией:

struct Device {
    buffer: [u8; 16],
}

struct Data<'a> {
    own: &'a mut Device,
}

impl<'a> RawData for Data<'a> {
    fn query(&mut self) -> Result<&mut Self> {
        Ok(self)
    }
    
    fn as_slice(&self) -> &[u8] {
        &self.own.buffer[..]
    }
    
    fn as_mut_slice(&mut self) -> &mut [u8] {
        &mut self.own.buffer[..]
    }
}

impl<'a> RawHoldingRegisters<'a> for Device {
    type Data = Data<'a>;
    fn put<A: Into<u16>>(&mut self, start_addr: A, registers: u16, bytes: u8) -> Self::Data {
        Data{own: self}
    }
    
    fn pop<A: Into<u16>>(&mut self, start_addr: A, registers: u16) -> Self::Data {
        Data{own: self}
    }
}

ошибка:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
  --> src/lib.rs:69:9
   |
69 |         Data{own: self}
   |         ^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 68:5...
  --> src/lib.rs:68:5
   |
68 | /     fn put<A: Into<u16>>(&mut self, start_addr: A, registers: u16, bytes: u8) -> Self::Data {
69 | |         Data{own: self}
70 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:69:19
   |
69 |         Data{own: self}
   |                   ^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 66:6...
  --> src/lib.rs:66:6
   |
66 | impl<'a> RawHoldingRegisters<'a> for Device {
   |      ^^
   = note: ...so that the types are compatible:
           expected RawHoldingRegisters<'a>
              found RawHoldingRegisters<'_>

код целиком - https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=76f9690f35bb0b324c26969f5ba5cca4

Два варианта:


Пока не реализуют Generic Associated Types придется обходится подобными костылями.


Нашел еще одно решение, но оно совсем уже хитро вывернутое.
Суть в том что мы убираем ассоциированный тип в типаже RawHoldingRegisters, а возвращаемое значение задаем через ассоциированный тип нового типажа DeviceObject<'_>. Это позволяет избежать засорения временами жизни с помощью Higher Ranked Trait Bound:

pub trait RawHoldingRegisters: for<'a> DeviceObject<'a> { ... }

Вот статья в которой описан похожий метод:
Solving the Generalized Streaming Iterator Problem without GATs

1 лайк

отличная статья,
спасибо за помощь!

Вопрос по специализации:
в коде уже было impl HoldingRegisters for Device, и когда появилась impl RawHoldingRegisters for Device

компилятор выдал ошибку:

error[E0119]: conflicting implementations of trait `HoldingRegisters` for type `Device`:
  --> src/lib.rs:85:1
   |
26 | impl<T> HoldingRegisters for T where T: RawHoldingRegisters {
   | ----------------------------------------------------------- first implementation here
...
85 | impl HoldingRegisters for Device {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Device`

вроде бы ни чего не нарушено, поскольку HoldingRegisters был реализован для T:
impl<T> HoldingRegisters for T where T: RawHoldingRegisters,
но не был реализован для конкретного Device,
или специализация работает не до конца полностью?

код

А с каких пор специализация вообще работает? ) Её же как небыло, так и нет.

А это разве не то:

Now, this works quite well when specializing a blanket impl with an impl for a concrete type:

impl<T> SomeTrait for T { /* default fns */ }
impl SomeTrait for SomeType { /* specialized fns */ }

отсюда

Неа, это не то. В этой серии статей идет обсуждение о решении проблем реализации специализации и об эргономике предлагаемых решений. До, собственно, самой реалиизации еще дело не дошло: https://github.com/rust-lang/rust/issues/31844