Skip to content
Merged
Changes from 16 commits
Commits
Show all changes
42 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
d7429f8
Clarify reason for intrinsic calls
scimind2460 Dec 30, 2025
be1092e
Apply suggestion from @programmerjake
joshtriplett Jan 6, 2026
b28187b
Remove polar from the main body of the RFC
joshtriplett Jan 6, 2026
be027bb
Apply suggestions from code review
scimind2460 Jan 7, 2026
b376ab9
Update 3892-complex-numbers.md
scimind2460 Jan 7, 2026
410b497
Apply suggestions from code review
scimind2460 Jan 7, 2026
11a2bfa
Refine complex numbers documentation and remove redundancy
scimind2460 Jan 7, 2026
a99270d
Update text/3892-complex-numbers.md
scimind2460 Jan 14, 2026
5480bd4
Revise alternatives section for complex numbers
scimind2460 Jan 15, 2026
9e3bacd
Enhance discussion on complex number alternatives
scimind2460 Jan 16, 2026
d773668
Expand alternatives section for complex numbers
scimind2460 Jan 17, 2026
7db4d02
Expand notes on complex number implementation
scimind2460 Jan 29, 2026
d1887a9
Enhance discussion on Complex number features
scimind2460 Feb 1, 2026
ef39bdf
Refine discussion on complex number features
scimind2460 Feb 1, 2026
5bbb202
Enhance RFC for complex numbers in Rust
scimind2460 Feb 26, 2026
2ddc399
Clean up whitespace, etc.
traviscross Mar 17, 2026
2c89ec3
Add tracking issue for RC 3892
traviscross Mar 17, 2026
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
252 changes: 252 additions & 0 deletions text/3892-complex-numbers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
- 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

Comment thread
scimind2460 marked this conversation as resolved.
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.
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
In essence, this RFC makes code like this:
Comment thread
scimind2460 marked this conversation as resolved.
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
```C
extern double _Complex computes_function(double _Complex x);
```
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))
Comment thread
joshtriplett marked this conversation as resolved.
Outdated
}
```
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).
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
```rust
let x = Complex::new(3.0, 4.0);
```
Comment on lines +36 to +44
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Discussion: where should this type live?

(redirecting discussion from the main PR thread to keep things organized)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I believe it should live in core::num: after all, it is a numeric type in the end, even though its main purpose is FFI.

They can even be passed as an array:
```rust
let y = Complex::from([3.0, 4.0]);
```
or as a tuple:
```rust
let z = Complex::from((3.0, 4.0));
```
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);
```
Comment thread
joshtriplett marked this conversation as resolved.
Outdated
They are added and multiplied as complexes are:
```rust
let first = Complex::new(1.0, 2.0);
let second = Complex::new(3.0, 4.0);
let added = first + second; // 4 + 6i
let multiplied = first * second; // -4 + 10i
```

They can be divided using normal floating-point division
```rust
let float_first = Complex::new(1.0, 2.0);
let float_second = Complex::new(3.0, 4.0);
let divided = float_second / float_first; // 2.4 - 0.2i
```

You can even calculate the complex sine, cosine and more:
```rust
let val = Complex::new(3.0, 4.0);
let sine_cmplx = csin(val); // 3.8537380379 - 27.016813258i
```
Comment thread
tgross35 marked this conversation as resolved.
Outdated
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(double _Complex x);
```
```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.0, 4.0))
}
```
Comment thread
scimind2460 marked this conversation as resolved.
Outdated

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

Complex numbers will be implemented by using traits in the `core` crate:
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
```
trait Float: Copy + Clone {}
impl Float for f32 {}
impl Float for f64 {}
```
Calls to some `libgcc` functions will also be needed and will be emitted by the backend via compiler-builtins, specifically `__mulsc3`, `__muldc3`, `__divsc3` and `__divdc3` for the proper and complete implementation of these types.
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
```
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
They will have an internal representation similar to this:
```rust
// in core::complex
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
#[lang = "complex"] // For matching the calling convention (special repr needed?)
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Complex<T: Float>(re: T, im: T);
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
```
have construction methods and `From` impls:
```rust
impl Complex<T> {
fn new(re: T, im: T) -> Self;
}

impl<T: Float> From<(T, T)> for Complex<T> {
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
fn from(value: (T, T)) -> Self;
}
impl<T: Float> From<[T; 2]> for Complex<T> {
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
fn from(value: [T; 2]) -> Self;
}
```

have methods to calculate their real and imaginary part (`.re()` and `.im()`):
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
```rust
impl<T: Float> Complex<T> {
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
fn re(self) -> T;
fn im(self) -> T;
}
```
polar conversions:
```rust
impl<T: Float + Mul + Add> Complex<T> {
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
fn modulus(self) -> T;
}

impl Complex<f32> {
fn angle(self) -> f32;
fn from_polar(modulus: f32, angle: f32) -> Self;
}

impl Complex<f64> {
fn angle(self) -> f64{
}
fn from_polar(modulus: f64, angle: f64) -> Self;
}
```
and have arithmetic implementations similar to this:
```rust
impl<T: Add + Float> Add for Complex<T> { // and for corresponding real types
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
type Output = Self;

fn add(self, other: Self) -> Self::Output;
}

impl<T: Sub + Float> Sub for Complex<T> { // and for corresponding real types
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
type Output = Self;

fn sub(self, other: Self) -> Self::Output;
}
impl Mul for Complex<f32> { // calls to __mulsc3 will be required here for implementation details and corresponding real types will also be implemented
type Output = Self;

fn mul(self, other: Self) -> Self::Output;
}
impl Mul for Complex<f64> { // calls to __muldc3 will be required here for implementation details and corresponding real types will also be implemented
type Output = Self;

fn mul(self, other: Self) -> Self::Output;
}
impl Div for Complex<f32> { // calls to __divsc3 will be required here for implementation details and corresponding real types will also be implemented
type Output = Self;

fn div(self, other: Self) -> Self::Output;
}
impl Div for Complex<f64> { // calls to __divdc3 will be required here for implementation details and corresponding real types will also be implemented
type Output = Self;

fn div(self, other: Self) -> Self::Output;
}
```
Comment thread
scimind2460 marked this conversation as resolved.
The floating point numbers shall have sine and cosine and tangent functions, their inverses, their hyperbolic variants, and their inverses defined as per the C standard and with Infinity and Nan values defined as per the C standard.
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
## Drawbacks
[drawbacks]: #drawbacks
Comment thread
scimind2460 marked this conversation as resolved.

The implementation surface of the complex types means more items for the libs and lang teams to maintain.
Also, the multiple emitted calls to `libgcc.so` (`__mulsc3` and the like) may cause a bit of overhead and may not be what the Rust lang team and compiler team want.

## Rationale and alternatives
Comment thread
scimind2460 marked this conversation as resolved.
[rationale-and-alternatives]: #rationale-and-alternatives

The rationale for this type is mostly FFI: C libraries that may be linked from Rust code currently cannot provide functions with direct struct implementations of Complex - they must be hidden under at least a layer of indirection. This is because of the undefined calling convention of complex numbers in C. For example: on powerpc64-linux-gnu, [returning double _Complex doesn't do the same thing as returning a struct with a field of type double[2].](https://gcc.godbolt.org/z/hh7zYcnK6) However, it is not always possible to write a C complex-valued function that wraps the first function in a pointer. Thus, FFI becomes a problem if such complex-valued functions are passed by value and not by reference.
Comment thread
scimind2460 marked this conversation as resolved.

You could theoretically do something like this:
```c
double _Complex function(double _Complex value);
double _Complex *wrapper_function(double _Complex* value) {
return &function(*value);
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
}
```
for all functions you wish for. But this still needs to happen in C.

### 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.
- Use a polar layout: Polar complex numbers, are undoubtedly a more optimal solution for multiplying complexes. However, I believe that if we wish to have proper FFI with C, then complex number layout should be chosen in accordance with the layout that is used in the C standard, and that is the orthogonal layout. This is also the layout used by most of other languages and crates on crates.io.
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
- Non-generic primitive types: These are, obviously, the most obvious and practical solution. However, if we implemented lots of such types, then we would not be able to expand for `f16` and `f128` support without repeating the code already implemented. It would be extremely repetitive and tedious to add new types, especially since Gaussian integers and other floating points could have added support.
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
Comment thread
scimind2460 marked this conversation as resolved.
Outdated

## 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.
Comment thread
scimind2460 marked this conversation as resolved.
Outdated
For example, in Python:
```py
complex_num = 1 + 2j
complex_second = 3 + 4j
print(complex_num * complex_second)
```
or in C:
```c
float _Complex cmplx = 1 + 2*I;
float _Complex two_cmplx = 3 + 4*I;
printf("%.1f%+.1fi\n", creal(cmplx * two_cmplx), cimag(cmplx * two_cmplx));
```
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)
Comment thread
scimind2460 marked this conversation as resolved.

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

Should this type be in `core::ffi`? This type's purpose is mostly FFI, but it might be useful in library contexts as well, so I am not sure if we should place it in `core::ffi`.

## 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? Or perhaps we could consider a constant:
```rust
impl<T: Float> Complex<T: Float> {
const I: T = Complex::new(T::zero(), T::one());
}
```
where `zero` and `one` is implemented on the `Float` trait similar to `num_traits`?
Or maybe we could have a method on normal numbers:
```rust
// for example
impl f32 {
fn i(self) -> Complex<f32> {
Complex::new(0, self)
}
}
```
that could help simplify the life of people who otherwise would have to keep writing `Complex::new()`?
- Should we support Imaginary eventually? This RFC doesn't cover it, but I think we can do this later in another RFC.
- Eventually we may support Gaussian integers (an extension of the real integers) which have a Euclidean division procedure with remainder. We could theoretically eventually support these integers?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'll note GCC has integer complex numbers as a C extension, which I think Rust should eventually support FFI with.

- We can also support f16 and f128 once methods for them are stabilised.
- We should also think about a `Display` implementation. Should we support something like `1 + 2i` or something else? Should we not make a `Display` impl at all, and just use re() and im() for the implementation?
- We should also consider adding aliases (like c32 and c64) for floating points once they are established, to allow for a shorthand syntax.