non-primitive cast error when using multiple key-value pairs in debug! macro inside async_trait #706

Open
opened 2025-10-18 00:18:00 -05:00 by xhermosilla · 3 comments
xhermosilla commented 2025-10-18 00:18:00 -05:00 (Migrated from github.com)

Description

When using the debug! (or other log-level) macro with multiple key-value pairs, compilation fails with the following error:

error[E0605]: non-primitive cast: `&[(&'static str, Value<'_>); 2]` as `&[(&str, Value<'_>)]`

However, when only one key-value pair is used, or when no key-values are included, it compiles and runs correctly.

### Example

This compiles fine:

debug!(host = "localhost", "Deleting documents");

But adding an additional key-value pair causes a compilation error:

debug!(host = "localhost", version = "1.0.0"; "Deleting documents");

This produces:

error[E0605]: non-primitive cast: `&[(&'static str, Value<'_>); 2]` as `&[(&str, Value<'_>)]`

Context

This happens inside a function annotated with #[async_trait], but the same pattern works fine outside of async traits.
It seems related to how Rust infers lifetimes and array types in macro expansions involving slices of tuples.

The generated code from the macro expands roughly into:

&[("host", host_value), ("version", version_value)] as &[(&str, Value)]

which causes the compiler to reject the cast because the inferred array type has 'static keys (&'static str) but the target expects &str.

### Workaround

Replacing the slice construction inside the macro with this expression fixes the issue:

&vec![
    ($crate::__log_key!($key), $crate::__log_value!($key $(:$capture)* = $($value)*))
] as &[_],

Using a vec![] and taking a reference seems to allow proper type coercion to &[(&str, Value)].

### Possible Cause

This likely comes from a type coercion mismatch between:
• the fixed array literal ([ ... ]) which has a concrete type like [(&'static str, Value<'>); N],
• and the expected trait-bound type &[(&str, Value<'
>)].

The compiler disallows this cast because arrays of tuples with different lifetimes on the first element are not directly coercible to slices of tuples with more general lifetimes.

Using vec![] allows dynamic slice coercion at runtime and avoids this limitation.

## Environment

•	Rust version: 1.81.0
•	Crate: log (latest release at time of writing)
•	Feature: kv enabled
•	Context: using within #[async_trait] functions
## Description When using the debug! (or other log-level) macro with multiple key-value pairs, compilation fails with the following error: ```rust error[E0605]: non-primitive cast: `&[(&'static str, Value<'_>); 2]` as `&[(&str, Value<'_>)]` ``` However, when only one key-value pair is used, or when no key-values are included, it compiles and runs correctly. ### Example This compiles fine: ```rust debug!(host = "localhost", "Deleting documents"); ``` But adding an additional key-value pair causes a compilation error: ```rust debug!(host = "localhost", version = "1.0.0"; "Deleting documents"); ``` This produces: ```rust error[E0605]: non-primitive cast: `&[(&'static str, Value<'_>); 2]` as `&[(&str, Value<'_>)]` ``` ### Context This happens inside a function annotated with #[async_trait], but the same pattern works fine outside of async traits. It seems related to how Rust infers lifetimes and array types in macro expansions involving slices of tuples. The generated code from the macro expands roughly into: ```rust &[("host", host_value), ("version", version_value)] as &[(&str, Value)] ``` which causes the compiler to reject the cast because the inferred array type has 'static keys (&'static str) but the target expects &str. ### Workaround Replacing the slice construction inside the macro with this expression fixes the issue: ```rust &vec![ ($crate::__log_key!($key), $crate::__log_value!($key $(:$capture)* = $($value)*)) ] as &[_], ``` Using a vec![] and taking a reference seems to allow proper type coercion to &[(&str, Value)]. ### Possible Cause This likely comes from a type coercion mismatch between: • the fixed array literal ([ ... ]) which has a concrete type like [(&'static str, Value<'_>); N], • and the expected trait-bound type &[(&str, Value<'_>)]. The compiler disallows this cast because arrays of tuples with different lifetimes on the first element are not directly coercible to slices of tuples with more general lifetimes. Using vec![] allows dynamic slice coercion at runtime and avoids this limitation. ## Environment • Rust version: 1.81.0 • Crate: log (latest release at time of writing) • Feature: kv enabled • Context: using within #[async_trait] functions
Thomasdezeeuw commented 2025-10-18 10:14:14 -05:00 (Migrated from github.com)

This seems more of a Rust/async_trait issue, than a log issue to be honest. If it works without the #[async_trait] attribute, that's the things that is breaking the code 🤷

As for the suggestion of using a vector, I don't consider it an option to allocate for each call to log, especially considering it works for normal and async functions already.

This seems more of a Rust/async_trait issue, than a log issue to be honest. If it works without the `#[async_trait]` attribute, that's the things that is breaking the code 🤷 As for the suggestion of using a vector, I don't consider it an option to allocate for each call to log, especially considering it works for normal and async functions already.
KodrAus commented 2025-10-19 05:28:29 -05:00 (Migrated from github.com)

I wonder if this is some variant of another macro lifetime related issue I noticed elsewhere recently. At some point it looks like the edition rules of the macro source crate were applied to expanded code in the target. Now it seems like the target crate's rules are applied, causing expanded code relying on older edition rules to be invalid.

I haven't been able to repro this yet, but am wondering if a change like this might fix it: https://github.com/rust-lang/log/pull/707

I wonder if this is some variant of another macro lifetime related issue I noticed elsewhere recently. At some point it looks like the edition rules of the macro source crate were applied to expanded code in the target. Now it seems like the target crate's rules are applied, causing expanded code relying on older edition rules to be invalid. I haven't been able to repro this yet, but am wondering if a change like this might fix it: https://github.com/rust-lang/log/pull/707
tisonkun commented 2026-06-01 22:41:56 -05:00 (Migrated from github.com)

@xhermosilla A complete repro should help.

I can't make a repro so I can't locate what is the root cause and how we can solve it.

Based on my experience, coding agent is good at making repros while previously I hated to make one :P

@xhermosilla A complete repro should help. I can't make a repro so I can't locate what is the root cause and how we can solve it. Based on my experience, coding agent is good at making repros while previously I hated to make one :P
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-rust-lang/log#706
No description provided.