Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@aarkegz
Copy link
Contributor

@aarkegz aarkegz commented Jan 20, 2026

Currently, the default implementations of methods in crate_interface traits cause some inconsistencies. They:

  • Can be called by other default implementations.
  • Can be called by the type implementing crate_interface traits.
  • Can be ignored when implementing crate_interface traits.
  • Can NOT be called by call_interface!.

It's frustrating in at least two ways: ignoring methods with default implementations in #[impl_interface] causes no compiler error, while calling them with call_interface! causes link-time errors (instead of calling the default implementations). It violates the principle of least surprise. Take the following snippet as an example:

#[def_interface]
pub trait If {
    fn a() -> i32 { 42 }
    fn b() -> i32 { Self::a() + 1 } // <- If::a can be used here
    fn c() -> i32;
}

pub struct Impl;

#[impl_interface]
impl If for Impl {
    // <- If::a can be ignored here without any compiler error
    fn b() -> i32 { 998244353 }
    fn c() -> i32 { Self::a() + 2 } // <- If::a can be used here
}

#[test]
fn caller() {
    assert_eq!(<Impl as If>::a(), 42); // <- If::a can be used directly via the implementor type
    assert_eq!(<Impl as If>::b(), 998244353);
    assert_eq!(<Impl as If>::c(), 44);

    assert_eq!(call_interface!(If::a), 42); // <- If::a CAN NOT be used here, link-time error
    assert_eq!(call_interface!(If::b), 998244353);
    assert_eq!(call_interface!(If::c), 44);
}

Also, providing default implementations for some crate_interface methods is extremely useful, especially when designing and implementing BSPs.

It's possible to use weak symbols as a solution: the default implementations are wrapped and exported in functions with same signature as the expected implementor and marked as #[linkage = "weak"], allowing the use of them with call_interface!, while implementations in #[impl_interface] blocks can safely override them. For backward compatibility, this feature is gated by the "weak_default" feature.

Possible drawbacks:

  • Required nightly rust and manual enablement of linkage , until Tracking issue for the linkage feature rust-lang/rust#29603 is stabilized.
  • Weak symbols cannot co-exist with strong symbols under the same name in the same Rust crate. As a result, it's impossible to override the default implementation when implementing the crate_interface trait in the same crate where it's defined. It seems OK for real-world scenarios, but makes tests a little bit more complex.

Possible improvements and fixes that can be shipped with this PR:

  • Use sub-attributes to explicitly mark which default implementations should be exported.
  • Disable all self-like receivers in all crate_interface methods.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for using weak symbols to enable default trait method implementations to be called via call_interface!. This feature is gated behind a weak_default feature flag and requires nightly Rust with the linkage feature.

Changes:

  • Added weak_default feature flag to Cargo.toml with documentation
  • Modified def_interface macro to generate weak symbol functions for methods with default implementations when the feature is enabled
  • Added validation to reject methods with self parameters when using weak_default
  • Added comprehensive test file for the new feature
  • Removed default implementation from test_crate_interface.rs to maintain test validity

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.

File Description
Cargo.toml Adds weak_default feature flag with inline documentation
src/lib.rs Implements weak symbol generation for default trait methods with self-parameter validation and feature-gated code
tests/test_weak_default.rs New test file demonstrating weak symbol functionality with partial trait implementations
tests/test_crate_interface.rs Removes default implementation from foo() method to maintain test validity without weak_default feature

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@AsakuraMizu
Copy link

For your reference, we implemented the "real" trait in https://github.com/Starry-OS/extern-trait by generating code using macro_rules!, so that the default implementation can be used.

@AsakuraMizu
Copy link

AsakuraMizu commented Jan 21, 2026

Another reference is that Rust's official implementation of external functions has already been implemented in nightly, with the feature name eii. It just supports a fallback default implementation. See: rust-lang/rust#125418

@aarkegz
Copy link
Contributor Author

aarkegz commented Jan 21, 2026

Another reference is that Rust's official implementation of external functions has already been implemented in nightly, with the feature name eii. It just supports a fallback default implementation. See: rust-lang/rust#125418

EII is still in its early stage, and I don't think we can migrate to eii in one or two years (RFC3635, for eii functions, and 3645, for eii traits, are not formally merged yet), so it's still worthy to make crate_interface better now.

- Introduced a new feature `weak_default` in Cargo.toml to generate weak symbol functions for trait methods with default implementations.
- Updated `def_interface` function in lib.rs to handle weak symbol generation, ensuring methods with `self` parameters are not supported.
- Modified test cases to reflect the updated trait method signatures.
@aarkegz
Copy link
Contributor Author

aarkegz commented Jan 22, 2026

Something to discuss: what's the expected behavior in the following case?

#[def_interface]
trait If {
    fn x() -> i32 { 42 }
    fn print_x() { println!("{}", Self::x()) }
}

struct Impl;

#[impl_interface]
impl If for Impl {
    fn x() -> i32 { 100 }
}

fn main() {
    call_interface!(If::print_x);
}

I prefer 42. I think the use of Trait::fn_with_default should always find the default implementation, while call_interface! and generated caller functions should find the override implementation.

Nevermind, let the user override it.

@aarkegz aarkegz marked this pull request as ready for review January 22, 2026 17:46
@aarkegz
Copy link
Contributor Author

aarkegz commented Jan 22, 2026

Known issue: calling associated methods in the default implementation of a method in the same trait results in compiler error. Expected to be fixed soon.

@aarkegz
Copy link
Contributor Author

aarkegz commented Jan 23, 2026

Known issue: calling associated methods in the default implementation of a method in the same trait results in compiler error. Expected to be fixed soon.

Already fixed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 27 changed files in this pull request and generated 9 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@aarkegz aarkegz changed the title Feature proposal: use weak symbols to allow using default trait methods Use weak symbols to allow using default trait methods Jan 23, 2026
@aarkegz aarkegz merged commit 259ec6e into main Jan 25, 2026
18 checks passed
@ZhiyuanSue ZhiyuanSue deleted the feat/weak_default branch January 27, 2026 07:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants