Skip to content
Open
Changes from 2 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ba634bb
Add complex numbers rfc
SciMind2460 Dec 2, 2025
0f4d922
Change file name and pr to reflect current issue
SciMind2460 Dec 2, 2025
863794c
Incorporate feedback into RFC
SciMind2460 Dec 3, 2025
60465d0
Fix errors
SciMind2460 Dec 3, 2025
66b4a8e
Remove impls and replace with stubs
SciMind2460 Dec 3, 2025
e6a51ea
Fix polars
SciMind2460 Dec 3, 2025
f63d8e6
Add more future possibilities and fix typos
SciMind2460 Dec 3, 2025
8033da5
Add associated consts and collapse functions
SciMind2460 Dec 3, 2025
d86a918
Apply suggestions from code review
SciMind2460 Dec 3, 2025
4add4e1
Add fixes in type signature
SciMind2460 Dec 4, 2025
5aceb3e
Apply suggestions from code review
SciMind2460 Dec 4, 2025
94dece5
Clarified some details regarding `i`
SciMind2460 Dec 4, 2025
0cf423a
Fix wording in complex numbers documentation
SciMind2460 Dec 4, 2025
512a3be
Add possible workaround
SciMind2460 Dec 5, 2025
ca8f9ba
Update rationale for Complex type in FFI context
SciMind2460 Dec 5, 2025
23f1e67
Update drawbacks section for Complex type proposal
SciMind2460 Dec 7, 2025
c055e1a
Improve clarity and rationale in complex numbers documentation
SciMind2460 Dec 9, 2025
e438f35
Apply suggestions from code review
SciMind2460 Dec 10, 2025
d2c671a
Update RFC on complex numbers support
SciMind2460 Dec 11, 2025
3911071
Apply suggestions from code review
SciMind2460 Dec 16, 2025
73f823f
Enhance rationale and examples for complex numbers API
SciMind2460 Dec 16, 2025
e69a67f
Add suggestion
SciMind2460 Dec 16, 2025
d1e41cb
Remove `this`
SciMind2460 Dec 16, 2025
a1774ac
Refactor Complex struct and update methods
SciMind2460 Dec 17, 2025
6851400
Apply suggestions from code review
SciMind2460 Dec 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 214 additions & 0 deletions text/3892-complex-numbers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
- Feature Name: complex-numbers
- Start Date: 2025-12-02
- RFC PR: [rust-lang/rfcs#3892](https://github.com/rust-lang/rfcs/pull/3892)
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)

## Summary
[summary]: #summary

FFI-compatible and calling-convention-compatible complex types are to be introduced into `core` to ensure synchronity with C primitives.

## Motivation
[motivation]: #motivation

The definition of complex numbers in the C99 standard defines the _memory layout_ of a complex number but not its _calling convention_.
This makes crates like `num-complex` untenable for calling C FFI functions containing complex numbers without at least a level of indirection (`*const Complex`) or the like.
Only in `std` is it possible to make an additional repr to match the calling convention that C uses across FFI boundaries.
Copy link

@SOF3 SOF3 Dec 3, 2025

Choose a reason for hiding this comment

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

Why is FFI compatibility limited to std only? It's just like how we have all the types in crate@libc. What necessitates complex becominga type in something like crate@libc?

Copy link
Member

Choose a reason for hiding this comment

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

it's like va_list (core::ffi::va_list::VaListImpl) can only be implemented in libcore.

In essence, this RFC makes code like this:
```C
extern double _Complex computes_function(x: double _Complex);
```
callable in Rust without indirection:
```rust
extern "C" {
fn computes_function(x: Complex<f64>) -> Complex<f64>;
}
fn main() {
let returned_value = computes_function(Complex<f64>::new(3, 4))
}
```
using the standard library's FFI-compatible complex numbers.

## Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

`Complex<T>` numbers can be instantiated as of any type using `Complex::new(re, im)` where `re` and `im` are of the same type (this includes all numbers).
```rust
let x = Complex::new(3, 4); // this instantiates them as integers, not floats!
```
They can even be passed as an array:
```rust
let y = Complex::from_array([3, 4]);
```
or as a tuple:
```rust
let z = Complex::from_tuple((3, 4));
```
They can even be passed in polar form (but only as a float):
```rust
let polar = Complex::from_polar(3.0, f32::PI/2.0);
```
But the easiest way to usually instantiate them is by using them like this:
```rust
let easy = 1 + 2.i()
```
where .i() turns a real number into a complex one transposing the real value to a complex value.

They are added and multiplied as complexes are:
```rust
let first = 1 + 2.i()
let second = 3 + 4.i();
let added = first + second; // 4 + 6.i()
let multiplied = first * second; // -4 + 10.i()
```

They can be divided using Gaussian division (if integers) and normal division (if floats):
```rust
let first = 1 + 2.i();
let second = 3 + 4.i();
let divided = second / first; // 2 + 0.i()
let float_first = 1.0 + 2.0.i();
let float_second = 3.0 + 4.0.i();
let divided = float_second / float_first; // 2.4 - 0.2.i()
```

If the values are floating point, you can even calculate the complex sine, cosine and more:
```rust
let val = 3 + 4.i();
let sine_cmplx = csin(3+4.i()); // 3.8537380379 - 27.016813258i
```
It's not too much of a problem to print them:
```
println!("{}, 1 + 2.i()); // prints 1 + 2i
```
If you want to call certain C libraries with complex numbers, you use this type:
```C
// in the C library
extern double _Complex computes_function(x: double _Complex);
```
```rust
// in YOUR Rust code
extern "C" {
fn computes_function(x: Complex<f64>) -> Complex<f64>;
}
fn main() {
let returned_value = computes_function(Complex<f64>::new(3, 4))
}
```

## Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

Complex numbers will be implemented by using public traits in the `core` crate that implement as an abstraction numeric features such as:
```
trait PrimNum: Add + Sub + Mul + Div + Neg + SimdElement + Copy + Clone {
fn zero();
fn one();
}
trait PrimFloat: PrimNum {
fn sin();
fn cos();
fn tan();
// etc!
}
```
Copy link

Choose a reason for hiding this comment

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

Suggested change
```

to properly classify all types complex numbers can be implemented on.
They will have an internal representation of a Simd array to increase speed:
```rust
// in core::complex
#[lang = "complex"] // For matching the calling convention
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Complex<T: PrimNum>(Simd<T, 2>);
```
have methods to calculate their real and imaginary part (`.re()` and `.im()`)
and have arithmetic implementations similar to this:
```rust
impl<T: PrimNum> Add for Complex<T> {
fn add(self, other: Self) {
Complex(self.0 + other.0)
}
}
impl<T: PrimNum> Add<T> for Complex<T> {
fn add(self, other: T) {
Complex(self.0 + Simd::from_array(other)
}
}
impl<T: PrimNum> Add<Complex<T>> for T {
fn add(self, other: Complex<Self>) {
Complex(Simd::from_array([self, 0]) + other.0)
}
}
impl<T: PrimNum> Sub for Complex<T> {
fn sub(self, other: Self) {
Complex(self.0 + other.0)
}
}
impl<T: PrimNum> Sub<T> for Complex<T> {
fn add(self, other: T) {
Complex(self.0 + Simd::from_array([other, 0])
}
}
impl<T: PrimNum> Sub<Complex<T>> for T {
fn add(self, other: Complex<Self>) {
Complex(Simd::from_array([self, 0]) - other.0)
}
}
impl<T: PrimNum> Mul for Complex<T> {
fn mul(self, other: Self) {
Complex(Simd::from_array([self.0.re() * other.0.re() - self.0.im() * other.0.im(), self.0.im() * other.0.re() + self.0.re() * other.0.im()]))
}
}
impl<T: PrimNum> Mul<T> for Complex<T> {
fn mul(self, other: T) {
Complex(self.0 * Simd::from_array(other, other))
}
}
impl<T: PrimNum> Mul<Complex<T>> for T {
fn mul(self, other: Complex<Self>) {
Complex(Simd::from_array(self, self) * other.0)
}
}
impl<T: PrimNum> Div<T> for Complex<T> {
fn div(self, other: T) {
Complex(self.0 / Simd::from_array(other, other))
}
}
impl<T: PrimNum> Div for Complex<T> {
fn Div(self, other: Self) {
(self * other.conj()) / (other.modulus() * other.modulus())
}
}
```
The floating point numbers shall have sine and cosine and tangent functions, their inverses, their hyperbolic variants, and their inverses defined as per the mathematical definitions.
## Drawbacks
[drawbacks]: #drawbacks

There is only one drawback I can think of, and that is it might cause churn:
if there is suddenly a standard-library Complex type, people may rush to include it in their current implementations, which would leave people behind if they didn't know about it. I really don't think this is a drawback though, since similar things have happened in Rust before: the inclusion of `OnceCell` in Rust, for example.

## Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives

Some people on IRLO believe that complex numbers should be part of the std due to them being a language feature in many other languages including C, C++ and Go. They also mention that if Rust aims for good FFI with C, then complex numbers are an important feature.
### Alternatives:
Don't do this: There are, obviously, millions of alternatives on crates.io, the foremost being `num-complex`. However, I believe that if we wish to support proper FFI with C, then a standard type that matches calling conventions with C complex numbers is an important feature of the language. Hence, I do not recommend this idea.

## Prior art
[prior-art]: #prior-art

FORTRAN, C, C++, Go, Perl and Python all have complex types implemented in the standard library or as a primitive. This clearly appears to be an important feature many languages have.
Even in Rust, it has been discussed two times in IRLO:
- [First discussion](https://internals.rust-lang.org/t/c-compatible-complex-types-using-traits/13757)
- [Second discussion](https://internals.rust-lang.org/t/standard-complex-number-in-std-library/23748)

Many crates, like `num-complex` also provide this feature, though it is not FFI-safe.
## Unresolved questions
[unresolved-questions]: #unresolved-questions

Is this layout for the complex numbers fine? I just intended it to increase speed for `Add` and `Sub`, but I'm not entirely sure whether Simd arrays are needed for this.
## Future possibilities
[future-possibilities]: #future-possibilities

- Maybe later on, we can think of adding a special custom suffix for complex numbers (`1+2j` for example), and using that as a simpler way of writing complex numbers if this RFC is accepted? This is very similar to how most languages implement complex numbers.
- Should we support Imaginary eventually? This RFC doesn't cover it, but I think we can do this later in another RFC.