Struggling with generics

Hey folks, not sure why my brain turns to mush whenever generics get even slightly complex, but it does. :slight_smile:
I'm working on voice support in tts-rs. There's currently some non-functional code there which fails to build under Linux, so if anyone would mind checking that out then that's probably the quickest way to figure out what I'm struggling with. Otherwise, I'll link to a few highlights.

My first error is here:

error[E0283]: type annotations needed                                                                                                
   --> src/backends/                                                                                      
114 |             self.stop()?;                                                                                                      
    |                  ^^^^ cannot infer type for type parameter `T` declared on the trait `Backend`                                 
    = note: cannot satisfy `_: VoiceImpl`                                                                                            
note: required by a bound in `Backend::stop`                                                                                         
   --> src/                                                                                                             
214 | pub trait Backend<T: VoiceImpl>: Clone {                                                                                       
    |                      ^^^^^^^^^ required by this bound in `Backend::stop`                                                       
218 |     fn stop(&mut self) -> Result<(), Error>;                                                                                   
    |        ---- required by a bound in this                                                                                        

Where do I need to specify the missing type there? Interesting that other functions in that impl aren't triggering it.

Next, here:

error[E0277]: a value of type `Vec<Voice<T>>` cannot be built from an iterator over elements of type `Voice<speech_dispatcher::Voice>`                                                                                                                                    
    --> src/backends/                                                                                     
213  |             .collect::<Vec<Voice<T>>>();                                                                                      
     |              ^^^^^^^ value of type `Vec<Voice<T>>` cannot be built from `std::iter::Iterator<Item=Voice<speech_dispatcher::Voice>>`                                                                                                                                
     = help: the trait `FromIterator<Voice<speech_dispatcher::Voice>>` is not implemented for `Vec<Voice<T>>`                        
note: required by a bound in `collect`                                                                                               
    --> /home/nolan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/                                                                                                                              
1741 |     fn collect<B: FromIterator<Self::Item>>(self) -> B                                                                        
     |                   ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`                                                
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement                 
94   | impl<T: VoiceImpl> Backend<T> for SpeechDispatcher where Vec<Voice<T>>: FromIterator<Voice<speech_dispatcher::Voice>> {       
     |                                                    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++         

To be fair, it does offer a hint there, but that does seem a bit ugly. I was hoping I could just box up this type and rely on the impl I added, but either my fundamental design is flawed or I'm missing something obvious.

Thanks for any help.

Self could implement the Backend<T> for different T. (In fact, you're implementing for any T: VoiceImpl.) The other methods don't throw the error as that's the only place you're calling a method of the trait.

You could try:

<Self as Backend<T>>::stop(self)?;

But see also the rest of this reply.

Well, you're implementing for any T: VoiceImpl, but collecting a Vec<Voice<SpdVoice>> -- SpdVoice cannot take the place of every T: VoiceImpl.

Are you sure you didn't want to

impl Backend<SpdVoice> for SpeechDispatcher {


If you apply the hint, that's probably the practical effect it will have.

1 Like

You are correct, that does indeed fix one problem. Now I have:

error[E0308]: mismatched types                                                                                                       
   --> src/                                                                                                             
265 | impl<T: VoiceImpl> Tts<T> {                                                                                                    
    |      - this type parameter                                                                                                     
314 |             Ok(backend)                                                                                                        
    |                ^^^^^^^ expected type parameter `T`, found struct `speech_dispatcher::Voice`                                    
    = note: expected struct `Tts<T>`                                                                                                 
               found struct `Tts<speech_dispatcher::Voice>`                                                                          
error[E0308]: mismatched types                                                                                                       
   --> src/                                                                                                             
265 | impl<T: VoiceImpl> Tts<T> {                                                                                                    
    |      - this type parameter                                                                                                     
269 |     pub fn new(backend: Backends) -> Result<Tts<T>, Error> {                                                                   
    |                                      --------------------- expected `Result<Tts<T>, Error>` because of return type             
316 |             backend                                                                                                            
    |             ^^^^^^^ expected type parameter `T`, found struct `speech_dispatcher::Voice`                                       
    = note: expected enum `Result<Tts<T>, Error>`                                                                                    
               found enum `Result<Tts<speech_dispatcher::Voice>, _>`                                                                 

How do I get back a Backend<SpdVoice> in this case? Without, of course, having to specify the type directly in this function which needs to handle any number of Backend implementations for different types?

Thanks again.

It's the same situation as the Vec collecting before; you said your implementation was going to be generic over any T: VoiceImpl, but you're actually using one specific type, SpdVoice.

Your API says:

// "I can give you a `Tts<T>` (or an error)"
pub fn new(backend: Backends) -> Result<Tts<T>, Error>

And your implementation header says:

// "I'm going to implement this for any `T` that satisfies `VoiceImpl`"
impl<T: VoiceImpl> Tts<T> 

But your method body is trying to return a Tts<SpdVoice>, not a Tts<T>.

To stretch a tired OO analogy, you opened a pet store and said "I can deliver any pet!", but you're only handing out cats. The compiler is not amused, and wants you to actually deliver on what you promised (by either delivering any pet, or by only promising to deliver cats).


Right, I believe I follow that much. :slight_smile: What I'm struggling with is that this particular API only returns Tts<SpdVoice> for this particular platform. Other platforms will have their own VoiceImpl.

Where my brain turns to mush is at the facade/implementation boundary. Maybe I should remove the type parameter from the facade itself and just return specific instances per platform? I'd try it myself and may get to it in an hour or two--I just feel like I've been playing a shell game trying to get the type parameter right on the backend implementations, then right again on the facade. :slight_smile:

Thanks for your help.

If there is exactly one type per platform, you may be able to do something like

#[cfg(target_os = "windows")]
type VoiceImpl = String;
#[cfg(not(target_os = "windows"))]
type VoiceImpl = Arc<str>;

OK, thanks, this helped quite a bit.

I realize now that I can't use generics here, because a single platform may indeed have multiple backend implementations.

I'll just go with a struct containing the various voice values, possibly hidden by a public impl with getters to keep it read-only.

Thanks again for helping me reason through this. :slight_smile:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.