extfn
is a Rust library that implements extension functions, allowing any* freestanding function to
be called as a.foo(b)
instead of foo(a, b)
just by adding #[extfn]
and renaming the first parameter to self
.
use extfn::extfn;
use std::cmp::Ordering;
use std::fmt::Display;
#[extfn]
fn factorial(self: u64) -> u64 {
(1..=self).product()
}
#[extfn]
fn string_len(self: impl Display) -> usize {
format!("{self}").len()
}
#[extfn]
fn sorted_by<T: Ord, F>(mut self: Vec<T>, compare: F) -> Vec<T>
where
F: FnMut(&T, &T) -> Ordering,
{
self.sort_by(compare);
self
}
fn main() {
assert_eq!(6.factorial(), 720);
assert_eq!(true.string_len(), 4);
assert_eq!(vec![2, 1, 3].sorted_by(|a, b| b.cmp(a)), vec![3, 2, 1]);
}
Click here to expand macros
use extfn::extfn;
use std::cmp::Ordering;
use std::fmt::Display;
trait factorial {
fn factorial(self) -> u64;
}
impl factorial for u64 {
fn factorial(self) -> u64 {
(1..=self).product()
}
}
trait string_len<_T1> {
fn string_len(self) -> usize
where
_T1: Display;
}
impl<_T1> string_len<_T1> for _T1 {
fn string_len(self) -> usize
where
_T1: Display,
{
format!("{self}").len()
}
}
trait sorted_by<T> {
fn sorted_by<F>(self, dummy1: F) -> Vec<T>
where
F: FnMut(&T, &T) -> Ordering,
T: Ord;
}
impl<T> sorted_by<T> for Vec<T> {
fn sorted_by<F>(mut self, compare: F) -> Vec<T>
where
F: FnMut(&T, &T) -> Ordering,
T: Ord,
{
self.sort_by(compare);
self
}
}
fn main() {
assert_eq!(6.factorial(), 720);
assert_eq!(true.string_len(), 4);
assert_eq!(vec![2, 1, 3].sorted_by(|a, b| b.cmp(a)), vec![3, 2, 1]);
}
A list of all supported function signatures can be found in tests/signatures.rs. Nearly everything I could think of is supported, with a few exceptions (see Fine Print).
Please report any edge cases where the "extfn transform" (add #[extfn]
and rename the first parameter to self
)
doesn't work.
The #[extfn]
macro essentially just converts a function into an extension trait with a single method.
This trait shares it's name with the extension function, allowing us to mark extension functions as pub
and to import
them just like regular functions using use example::add1;
, maintaining the illusion:
mod example {
use extfn::extfn;
#[extfn]
pub fn add1(self: usize) -> usize {
self + 1
}
}
use example::add1;
fn main() {
assert_eq!(1.add1(), 2);
}
Extension functions are already implemented in other programming languages:
As a Rust feature, extension functions have been proposed here, here, here, here.
- Const functions are unsupported because of E0379
self: T::Assoc
is unsupported