Types
Feng is statically typed. Types are determined at compile time, and conversions between types must be written explicitly.
Built-in Scalar Types
| Category | Types | Common aliases |
|---|---|---|
| Signed integers | i8, i16, i32, i64 |
int |
| Unsigned integers | u8, u16, u32, u64 |
byte, uint |
| Floating point | f32, f64 |
float, double |
| Boolean | bool |
— |
| String | string |
— |
The widths of int and uint depend on the platform: they map to 32-bit types on 32-bit platforms and 64-bit types on 64-bit platforms. Integer literals infer as int by default, and floating-point literals infer as double.
let count: i32 = 42;
let size: uint = (uint)count;
let ratio: f64 = 0.5;
let ready = true;
At runtime, integer overflow wraps at the type's bit width. A literal known at compile time to be out of range is rejected.
Strings
string is an immutable UTF-8 string with a default value of "". Double-quoted strings support \\, \", \n, \r, \t, \0, and hexadecimal byte escapes. Backtick strings preserve their raw contents.
let line = "first\nsecond";
let path = `C:\data\feng`;
let message = "Hello, " + "Feng";
After importing std.text, methods for length, searching, splitting, and case conversion are available. String length is measured in UTF-8 bytes. Use the standard library's rune or grapheme APIs when you need a Unicode code-point or grapheme-cluster view.
Arrays
T[] is a fixed-length array whose current level is read-only. T[!] is a fixed-length array whose current level is writable. Arrays are managed reference types: assignment copies the reference, not the elements.
let values: int[] = [1, 2, 3];
let buffer: byte[!] = byte[:1024];
let matrix: int[!][!] = [[1, 2], [3, 4]];
buffer[0] = (byte)65;
matrix[0][1] = 9;
Type[:length] creates a writable array of the specified length. An array's length does not change after creation. Use std.collections.List<T> when you need a growable collection.
Write permission can be removed only with an explicit conversion:
let writable: int[!] = [1, 2, 3];
let readonly = (int[])writable;
Named Tuples
Feng has no anonymous tuple types. Declare a named tuple with the parenthesized form:
type Point(f64, f64);
type Pair<T, U>(T, U);
let origin: Point = (0.0, 0.0);
let item: Pair<int, string> = (1, "one");
println("{0}", origin.item1);
Named tuples are value types, and their elements are always immutable. A var binding can replace the entire tuple but cannot modify an individual element in place.
Enumerations
An enumeration defines a distinct named type:
enum Status {
Pending,
Running,
Done
}
let status = Status.Running;
let raw = (int)status;
All enum cases must either use implicit incrementing values or specify explicit integer literals; the two forms cannot be mixed. An explicit conversion from enum to int is allowed, but a conversion from int to enum is not.
User-Defined and Contract Types
Object types, spec contracts, union types, and generics are covered in User-Defined Types, Contracts and fit, Pattern Matching, and Generics.