Rust FFI 바인딩을 위한 함수 오버로딩 실험과 nightly에서의 splat 기능 사용 방법을 소개합니다.
Rust Foundation의 Rust-C++ 상호운용성 이니셔티브와 협력하여, Rust 프로젝트는 FFI 바인딩을 위한 함수 오버로딩을 실험해 왔습니다. 이제 이 실험은 컴파일러 및 상호운용성 도구 개발자가 함수 오버로딩을 탐색하기 시작할 수 있는 단계에 이르렀습니다.
안정 Rust는 이미 튜플과 트레이트를 사용하는 오버로딩의 한 형태를 지원하지만, 오버로드된 인수를 하나의 튜플 인수로 전달해야 하므로 이러한 오버로드 함수를 호출하는 방식이 낯설게 보입니다. 예: hypot((2.0, 3.0, 6.0)). 안정 Rust는 Add (+ 연산자) 및 Neg (- 값 부정 연산자) 같은 트레이트를 통해 사용자 정의 타입으로 내장 연산자를 오버로딩하는 것도 허용합니다.
다음과 같은 질문에 답하기 위해 불안정한 nightly Rust 언어 실험을 진행하고 있습니다.
yeet의 전통에 따라(자전거 보관소 논쟁을 피하기 위해), 실험의 첫 단계에서는 기본 문법인 #[rustc_splat] 속성을 사용하고 있습니다. 이 실험이 유용한 결과를 만들어 낸다면, 나중에 대체 문법을 고려할 수 있습니다.
2026-07-31 이후의 Rust nightly 빌드에는 미완성인 "splat" 컴파일러 기능을 사용하여 더 인체공학적인 함수 및 메서드 오버로딩을 지원하는 실험적 기능이 포함되어 있습니다. 컴파일러 또는 상호운용성 도구 개발자라면 이 기능을 실험해 보시기를 권장합니다!
이 실험에서는 hypot(2.0, 3.0, 6.0)처럼 오버로드된 함수를 분리된 인수로 호출할 수 있습니다. 이중 괄호가 필요 없습니다! 다만 타입 추론과 타입 검사는 안정 Rust의 오버로드에서와 동일하게 수행됩니다.
구현의 복잡도를 파악하고 일부 언어 상호운용성 사용 사례를 해결하는지 확인하기 위해 splat을 실험하고 있습니다. 대부분의 Rust 언어 실험처럼, 이 nightly 기능에는 RFC가 없으며 언제든 변경되거나 제거될 수 있습니다.
이 기능은 앞으로 크게 변경되거나 더 인체공학적인 인터페이스로 대체될 것으로 예상합니다. 이러한 취지에서 Rust 프로젝트 Outreachy 인턴인 Ajay Singh는 splat 기반 오버로딩을 더 인체공학적으로 만드는 매크로를 작업하고 있습니다. 그의 작업은 GitHub의 rust-foundation/overloading-macros 저장소에서 확인할 수 있습니다.
이 기능의 설계 공리는 다음과 같습니다.
프로그래밍 언어마다 오버로드 결정 규칙이 다르므로, 이는 어려운 설계가 될 수 있습니다.
Rust 오버로딩은 다양한 프로그래밍 언어를 지원하는 것을 목표로 합니다. 이 예시에서는 널리 알려져 있고 기존 상호운용성 도구가 상당히 많은 C++를 사용합니다.
다음은 "splat"을 사용해 Rust에서 해당하는 오버로드를 만들고, 오버로드된 C++ hypot(빗변) 함수를 호출하는 Rust 코드입니다.
#![feature(splat, tuple_trait)]
#![expect(incomplete_features)]
use cpp::cpp;
use std::{ffi::c_double, marker::Tuple};
cpp! {{ #include <cmath> }}
/// The arguments of an overloaded C++ `hypot` function.
trait HypotArgs: Tuple {
type Output;
fn call_hypot(self) -> Self::Output;
}
/// Calls the overloaded C++ `std::hypot` function with the given arguments.
fn hypot<Args: HypotArgs>(#[rustc_splat] args: Args) -> <Args as HypotArgs>::Output {
args.call_hypot()
}
/// A 2-argument `hypot` overload.
impl HypotArgs for (c_double, c_double) {
type Output = c_double;
fn call_hypot(self) -> c_double {
let (x, y) = self;
unsafe {
cpp!([x as "double", y as "double"] -> c_double as "double" {
// This is C++ code!
return std::hypot(x, y);
})
}
}
}
/// A 3-argument `hypot` overload.
impl HypotArgs for (c_double, c_double, c_double) {
type Output = c_double;
fn call_hypot(self) -> c_double {
let (x, y, z) = self;
unsafe {
cpp!([x as "double", y as "double", z as "double"] -> c_double as "double" {
return std::hypot(x, y, z);
})
}
}
}
fn main() {
println!("|(3, 4)| = {}", hypot(3.0, 4.0));
println!("|(2, 3, 6)| = {}", hypot(2.0, 3.0, 6.0));
}
이 예시는 cpp 크레이트를 사용하여 Rust 파일 안에 C++ 코드를 인라인으로 작성합니다. 완전히 실행 가능한 예시는 GitHub에서 제공됩니다. 최소한의 Rust 전용 예시도 온라인에서 Rust 플레이그라운드로 실행할 수 있습니다.
최근 "splat"에 관해 작성한 글에 포함된 Nadrieril의 원래 "Overloading at Home" 코드 또한 Rust 플레이그라운드에서 실행할 수 있습니다.
위에서 언급했듯이 splat은 현재 미완성 컴파일러 기능이며 nightly Rust 컴파일러에서만 사용할 수 있습니다. splat은 또한 인체공학적이지 않으며, 다음 설계 단계의 일부로 이를 변경하고자 합니다.
rustdoc에서 splat 함수 인수를 지원하는 기능은 8월 12일에 막 병합되었습니다. splat 인수는 인수 이름 대신 줄임표(…)로 표시됩니다. 이 문법은 불안정하며 현재 rustdoc 표시용으로만 사용되고, 언제든 변경될 수 있습니다. 예를 들면 다음과 같습니다.
fn example(#[rustc_splat] args: (u32, String));
다음과 같이 표시됩니다.
fn example(…: (u32, String));
최근에는 함수 포인터를 위한 splat 지원도 병합했습니다. 내부 컴파일러 오류가 발생한다면 최신 nightly로 업데이트해 주세요. 함수 포인터 오버로딩 지원이 필요하다면, Zulip의 #t-lang/interop 채널에서 사용 사례를 알려 주세요.
또한 가변 인수 smallest 및 greatest 함수에 splat을 사용하는 Rust 표준 라이브러리 실험이 진행 중입니다. 이 함수들은 곧 nightly에서 사용할 수 있게 될 것으로 기대합니다.
실험 참여자들은 다른 버그도 발견할 것입니다. 지난 몇 달 동안 이미 일부가 발견되었습니다. 또한 일부 오버로딩 기능은 현재 범위 밖입니다. 버그나 제한 사항을 발견했다고 생각한다면 Zulip의 #t-lang/interop 채널에서 문의해 주세요.
이 작업은 Rust Foundation의 Rust-C++ 상호운용성 이니셔티브에 대한 Google의 아낌없는 자금 지원과 후원이 없었다면 가능하지 않았을 것입니다.
이 작업은 FFI 바인딩에서의 함수 오버로딩을 위한 Nightly 지원이라는 Rust 프로젝트 목표에 의해 추진되며, C++/Rust 상호운용성 문제 영역 매핑 Rust 프로젝트 목표의 결과물입니다.
또한 Oli, Ajay, Nadrieril, Scott, Taylor, Tyler, Matthias, Tim, Devin, Ralf, Jacob, Zachary를 비롯하여 제안, 피드백, 테스트, 버그 보고, 검토를 제공한 수많은 프로젝트 구성원 등 오버로딩 작업에 기여한 모든 분께 감사드립니다.
#[overload]
impl f64 {
/// Returns the 2-dimensional distance from the origin.
fn hypot(self, y: f64) -> f64 { … }
/// Returns the 3-dimensional distance from the origin.
fn hypot(self, y: f64, z: f64) -> f64 { … }
}
(이 예시는 Taylor Cramer가 공유한 설계의 변형입니다.)
빛나는 미래에는 Rust에 #[overload] 속성이 생겨 같은 이름을 가진 오버로드된 외부 함수를 호출할 때 "그냥 작동"할 수도 있습니다. 트레이트, 튜플, #[rustc_splat]이 필요 없으며 컴파일러가 모든 것을 처리합니다. 이 작업은 상호운용성을 대상으로 하므로, 처음에는 오버로딩이 extern 블록으로 제한될 수 있습니다. 네이티브 Rust로 확장하는 것은 별도의 기능이 될 수 있으며, 더 긴 기간에 걸쳐 안정화될 수도 있고 전혀 안정화되지 않을 수도 있습니다.
이는 컴파일러 구현 내부를 숨기는 매크로를 통해 이루어질 수도 있고, 전혀 매크로가 필요하지 않을 수도 있습니다. 기능의 최종 형태가 무엇일지는 예측하기 어렵습니다. 이제 막 첫 번째 실험을 시작했을 뿐입니다.
오버로딩 설계를 자세히 논의하려면 아직 해야 할 일이 많습니다. "splat" 실험은 Rust 타입 시스템의 한계, 일반적인 외부 언어 오버로드를 처리하는 방식, 오버로딩 사용자를 안내하는 데 필요한 진단, 그리고 향후 작업을 위한 설계상의 공백을 파악하는 데 도움이 될 것입니다.
또한 오버로딩이 채택된다면 그 문법을 찾아야 합니다. 함수 오버로딩 사용자들이 많이 발견했듯이, 이름을 짓는 일은 어렵습니다 😅
블로그의 향후 상호운용성 업데이트를 기대해 주세요. 프로젝트 목표 전체 목록은 여기에서 확인할 수 있으며, 그중 다수는 다양한 프로그래밍 언어와의 더 나은 상호운용성을 향해 나아가고 있습니다.