Cargo clippy succeeds but cargo build fails

github link for full code

pub enum Resp {
    SimpleString(SimpleString),
    BulkString(BulkString),
    Array(Array),
}

impl Resp {
    // This calls Array::parse_body
    pub fn parse(
        mut read: impl AsyncBufRead + Unpin + Send,
    ) -> impl Future<Output = Result<Self>> + Send { Box::pin(async move {...}) }
}

pub struct Array(Vec<Resp>);

impl RespParsable for Array {
    // This calls Resp::parse
    async fn parse_body(mut read: impl AsyncBufRead + Unpin + Send) -> Result<Self>
    where
        Self: Sized, { ... }
}

I tried to build a simple Redis Resp enum by defining async Resp::parse() -> Result<Self> and async Array::parse_body() -> Result<Self>, but I've had E0275 so I boxed Resp::parse in order to break the cycle. (You can see the code with E2705 error here)

After that cargo clippy has no error but cargo build has following error:

error: reached the recursion limit while instantiating `std::ptr::drop_in_place::<{async block@src/resp/mod.rs:34:18: 55:10}> - shim(Some({async block@src/resp/mod.rs:34:18: 55:10}))`
   --> /(userpath)/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:514:1
    |
514 | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
note: `std::ptr::drop_in_place` defined here
   --> /Users/jcshin/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:514:1
    |
514 | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

What is this error message and how can I fix it?

Filling in the missing parts, I cannot reproduce this. The code can be compiled in isolation, as well as if it's actually being called: Playground

I forgot to use playground - sorry, here's the playground. I also realized that removing the call to the Resp::parse makes the code build without error.

It's weird - I've got following results.

  1. Playground cargo build - success
  2. Playground cargo run - compile failure
  3. M1 Mac cargo clippy - success
  4. M1 Mac cargo build/run - compile failure

Oh, that's easy. If you remove the unnecessary references, then your code builds and causes a stack overflow. You have infinite mutual recursion.


It's simply that due to generics (and the recursive functions calling each other directly), it happens to manifest in the type system as well if you try to take references, because:

  • given the reader type R,
  • you call Resp::parse(R)
  • which then calls Array::parse_body(&mut R)
  • which then calls Resp::parse(&mut &mut R)
  • which then calls Array::parse_body(&mut &mut &mut R) etc.

In general, even if you don't write infinitely recursive code, don't take references gratuitously, and especially don't force users of your API to provide references if it's not needed.

3 Likes

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.