Release 0.2¶
Incan 0.2 is the release where the language surface becomes more explicit, more modular, and much more serious about Rust interop.
The headline breaking change is that stdlib imports and decorators now live under the std.* namespace. That affects everyday code immediately, but it is not cosmetic churn. It is the release boundary that moves Incan away from a compiler with lots of baked-in domain knowledge and toward a language where web, testing, async, derives, and trait surfaces are authored and imported as named library modules.
If you already know Python and Rust, the point of 0.2 is not to hide Rust from you. It is to give you a Python-shaped language with a clearer path into Rust crates, Rust types, and Rust-backed capabilities, while still treating borrowing and most Rust-specific mechanics as compiler concerns rather than user-facing bookkeeping.
That same theme shows up across the rest of the release:
- projects can now distinguish Incan library dependencies from Rust crate dependencies in
incan.toml - Rust interop is no longer just an escape hatch; it has a clearer authoring model, stronger docs, and much better compiler/runtime support
- library projects can export public APIs for other Incan projects to consume
- modules can now own runtime state through
static - generic calls can now spell explicit call-site type arguments
- the stdlib/compiler boundary is much cleaner than it was in
0.1
If you are migrating from 0.1, focus on the migration section first. If you want the short version, most existing projects need three concrete updates:
- move stdlib imports and decorators to
std.* - rename Rust dependency tables in
incan.toml - add explicit
std.async/std.testingimports in files that use those language surfaces
For the core reference material, start with Imports and modules, the standard library reference, and the language reference index.
What 0.2 is about¶
The 0.1 line proved out the compiler, core language, and early tooling. 0.2 turns that into a more coherent platform:
- the stdlib is no longer presented as a bag of magical global names
- compiler features that used to feel special-cased are pushed toward import-driven library surfaces
- the Rust boundary is more explicit: crate dependencies,
rust::imports,rusttype,interop:adapters, and trait/capability-oriented interop all fit together better than they did in0.1 - projects now have a clearer dependency model and a first real story for sharing Incan code across repositories
- the language picks up a few missing “this should just exist” capabilities such as
static, list concatenation, list extension, and explicit call-site generic arguments
This is still an incremental release rather than a final shape. Libraries are explicitly experimental, some runtime/library edges are still being refined, and 0.3 will continue improving ownership ergonomics and generated-runtime behavior. But 0.2 does make the language and tooling feel more coherent than 0.1.
If you want the broader conceptual backdrop, the best follow-up pages are Imports and modules, Static storage, Derives and traits, and How Incan works.
Migrating from 0.1¶
Use this when moving a 0.1 codebase to 0.2.
1. Move stdlib imports to std.*¶
All stdlib imports move from bare module names to the std.* namespace:
# Before (0.1)
from web import App, Response, Json
from testing import assert_eq, assert_ne
# After (0.2)
from std.web import App, Response, Json
from std.testing import assert_eq, assert_ne
This also applies to async modules:
# Before (0.1)
from async.time import sleep
# After (0.2)
from std.async.time import sleep
Types like App, Response, Html, Json, and Query are no longer implicitly available. Import them from std.web in each file that uses them.
See also: Imports and modules (reference), Stdlib index
2. Move decorators onto their namespaced modules¶
Decorators now resolve by module path rather than as ambient global names. The preferred style is to import the decorator and use the short local name:
from std.web.routing import route
@route("/hello")
async def hello() -> str:
return "Hello!"
The canonical fully qualified path also works:
@std.web.routing.route("/hello")
async def hello() -> str:
return "Hello!"
The same rule applies to testing markers:
import std.testing as testing
@testing.skip("not implemented")
def test_future() -> None:
pass
@skip, @xfail, @slow, @fixture, and @parametrize are test-runner metadata from std.testing. They are not runtime function calls.
See also: std.testing (reference), Testing in Incan
3. Split Incan dependencies from Rust dependencies¶
0.2 separates Incan Library dependencies from Rust crate dependencies in incan.toml:
[dependencies]is now for Incan libraries[rust-dependencies]is for Rust crates[rust-dev-dependencies]replaces[dev-dependencies]for Rust dev crates
If you leave Rust crates under [dependencies], the CLI emits a targeted migration error instead of silently accepting the old structure.
This is also the release where the Rust boundary becomes easier to understand as a system: Rust crates belong in manifest-level Rust dependency tables, while Incan code imports them through from rust import ... / from rust::... import ... and can describe wrapped Rust types with rusttype.
See also: Rust interop, Understanding Rust types (coming from Python)
4. Import std.async and std.testing in the files that use them¶
async / await and assert are import-activated surfaces in 0.2.
If a file uses async syntax, import std.async or one of its submodules in that same file. If a file uses the assert statement or testing decorators, import std.testing in that file as well.
See also: Async programming, std.async (reference)
5. Avoid reserved namespace names¶
std and rust are now reserved at module scope and can no longer be used as declaration names.
Major additions¶
A namespaced, source-authored stdlib¶
The most important architectural change in 0.2 is that stdlib features are now presented as explicit modules instead of ambient compiler surface. Web, testing, async, derives, traits, and related surfaces are increasingly defined through ordinary stdlib modules and metadata rather than bespoke compiler branches. That gives users a more readable import story and gives contributors a cleaner place to evolve the standard library.
For the new stdlib surface, start with std.* module reference, std.testing, std.async, std.traits, and std.derives.
A much stronger Rust interop story¶
If 0.1 established that Incan could talk to Rust, 0.2 is the release where that boundary becomes much more usable in practice.
You can now describe Rust-backed types more directly, declare Rust crate dependencies more clearly, import Rust items with better diagnostics, carry more borrowed-type information through the pipeline, and rely on much stronger docs for rusttype, interop: adapters, coercions, and capability-oriented generic bounds. That matters because Rust interop is not a niche escape hatch in Incan; it is part of the main workflow for real projects.
For that part of the release, start with Rust interop, Understanding Rust types (coming from Python), Traits and derives reference, and std.traits.
Incan Libraries (Experimental)¶
0.2 introduces the first cut of Incan Libraries. You can now build an Incan project as a library and consume its public API from another Incan project. The current scope is intentionally conservative, but it is the first real answer to “how do I share Incan code across projects?”
Building a library¶
incan build --lib path/to/project
This generates a .incnlib library manifest describing the public API of your library.
Consuming a library¶
Add the library to your incan.toml:
[dependencies]
mylib = { path = "../mylib" }
And import it in your Incan code using the pub:: namespace prefix:
from pub::mylib import my_function
def main() -> None:
my_function()
Library support is still early. The current release is built around local path dependencies and public API manifests, not a full package ecosystem.
For adjacent docs, the best starting points are Imports and modules and Build your first API, which show the project/module organization style 0.2 now leans into.
Module Static Storage¶
0.2 adds a new top-level declaration form: static (RFC 052, #242).
Use static when a module needs one live storage cell that persists across calls, for example counters, registries, caches, or shared mutable collections.
static is intentionally distinct from const:
constremains compile-time, deeply immutable, and baked into the output programstaticis runtime-initialized, live, and mutable through the ordinary assignment, method, and indexing paths
This release also includes pub static, so library modules can export live shared state through library manifests and pub:: imports rather than only exporting frozen compile-time constants.
Example:
pub static hits: int = 0
pub def record_hit() -> None:
hits += 1
from analytics import hits, record_hit
def main() -> None:
record_hit()
println(hits) # reads the current live value
See also: Static storage (reference), Module state, Static storage (explanation)
Call-site type arguments (RFC 054, #266)¶
Calls and method calls may now supply explicit generic type arguments in brackets immediately before the value argument list, for example id[int](1) or box.get[int](1). The _ placeholder still allows inference for selected positions, so forms like pair_map[int, _](1, 2) remain concise. Unsupported call forms now fail clearly instead of silently ignoring explicit brackets. See Call-site type arguments for the full rules and examples.
Further reading¶
Other 0.2 areas that are worth reading directly in the docs are Rust interop, Reflection, Traits and derives, book chapter: modules and imports, and book chapter: traits and derives.
Detailed inventory¶
The sections above are the release story. The list below is the detailed inventory of language, compiler, runtime, tooling, and docs changes that shipped in 0.2, grouped roughly by theme rather than by the order work happened to land.
Core language and stdlib surface¶
- Compiler: Namespaced stdlib imports under
std.*— all stdlib modules use qualified paths (RFC 022). - Compiler: New
statickeyword for module-owned runtime state, pluspub staticexports through library manifests andpub::imports (RFC 052, #242). See Module Static Storage. - Parser/formatter: Parenthesized multi-line import lists are supported for both
from module import (...)andfrom rust::crate import (...), with optional per-item aliases and formatter-aware wrapping (#116). - Compiler: Import-activated soft keywords —
asyncandawaitare identifiers by default and become reserved only whenimport std.async(orfrom std.async...) is present. Forgetting the import produces a targeted diagnostic with a fix suggestion. Similarly,std.testingactivates theassertstatement syntax (RFC 023). - Compiler: Surface Semantics Engine — soft keywords (
assert,async,await) and decorator semantics are routed through a registry-driven dispatch system (SurfaceSemanticsPack). The parser emits genericSurfaceAST nodes keyed bySurfaceFeatureKey; downstream stages dispatch on that key rather than per-keyword AST variants. Adding a new soft keyword requires no parser, AST, or IR changes — only a keyword descriptor and a semantics-pack handler (RFC 023). - Compiler:
incan-vocabestablishes a standalone, registry-driven keyword/manifest/desugaring contract for future library vocabulary extensions, and core tooling consumers now read keyword surface data through that shared registry path (RFC 027, #161). - Compiler: First-class named function references — named
deffunctions can now be passed as values, stored in variables, and placed in collections without wrapping in a closure.Callable[Params, R]is accepted as syntactic sugar for(Params) -> Rand desugared at parse time. Generic function references are explicitly rejected with a clear diagnostic (RFC 035, #169). - Compiler: Explicit call-site type arguments on direct calls and method calls (
name[T](...),recv.method[T](...)), including_placeholders for positions inferred from value arguments; unsupported call forms error instead of ignoring brackets (RFC 054, #266). See Call-site type arguments. - Runtime / Compiler: Strict collection indexing/method failures and strict string-to-number conversions now surface canonical Incan runtime diagnostics such as
KeyError,IndexError, andValueErrorinstead of leaking raw backend panic wrappers (#81). - Compiler: Traits are now always abstract, trait names and generic trait instantiations are valid annotation types, and traits may adopt supertraits with
with, enabling transitive capability hierarchies likeDataSet[T]/BoundedDataSet[T](RFC 042, #179). - Compiler: Namespaced decorator resolution — decorators resolved by module path (e.g.,
from std.web.routing import routeenables@route(...)) with alias support (RFC 022). - Compiler:
@rust.externmarker for Rust-backed function stubs, replacing the internal@std.builtindecorator (RFC 022, RFC 023). - Compiler:
@staticmethoddecorator for type-scoped methods with noselfreceiver, available onclass,model, andnewtypedeclarations. Required for@rust.externmethods on types (instance delegation is not supported). - Testing:
std.testingis now surface-first: assertion and marker-facing API is defined in ordinary stdlib source, while runtime-backed helpers are kept only where required for semantics.fail_tremains the panic host boundary, marker functions keep metadata-backed runtime entry points, andassert_raisesis tracked as a known blocker until parser-levelassert <expr> raises <Type>support and panic payload capture are both implemented. (#302) - Runtime / Compiler:
std.serde.json.Serializenow provides a source-defined defaultto_json()path for explicitwith Serializeadopters, while lowering forwards that trait adoption into the matching Rustserde::Serializederive so the direct-interop body compiles honestly.Deserialize.from_json()remains the irreducible host boundary for now. (#303) - Compiler / Stdlib:
std.reflectionnow typechecks__class_name__()and__fields__()against their real surface types (strandFrozenList[FieldInfo]) instead of backend-only unknown fallbacks. Plain reflection use no longer requires importingFieldInfo; explicit type annotations still do. (#304) - Compiler / Stdlib:
std.derives.{comparison,copying,string}no longer model derive-provided methods asrust.module()helper shims. These traits now live as source-defined capability contracts, while codegen continues to realize@derive(...)through ordinary Rust derive attributes on the adopting type. (#305) - Compiler / Stdlib:
std.traits.*now has a curated stdlib reference entry, andstd.traits.{convert,ops,error,indexing,callable,prelude}now has compiled-source coverage proving they compile directly from.incnsource withoutrust.module()/@rust.externhelper shims.std.traits.convertnow closes through@classmethodconversion hooks forfrom/try_from; broader Rust trait-impl authoring on wrappers remains RFC 043 work. (#306, #344) - Testing / Stdlib: RFC 023’s remaining behavioral-equivalence lane is now covered by an integration fixture that exercises migrated source-defined stdlib behavior end-to-end: explicit
std.serde.json.Serializeadoption,std.reflectionmetadata, and derive-backedClone/Eq/Ord/Defaultbehavior on the same model. (#158) - Runtime / Compiler:
std.mathcloseout alignment now keeps direct Rust interop wrappers while ensuring the module contract is consistent across typechecking, codegen, and docs (includinground,log2,atan2, andhypot; uppercase constants such asmath.PI) (#307). - Runtime / Compiler: Issue #301
std.asyncdirect-interoperability closeout forstd.async.timeandstd.async.selectis complete. These modules now expose their runtime-facing API via direct Rust imports (clamp_seconds/clamp_millishelpers,tokiotime primitives) while preserving the publicstd.async.*contract and error/return behavior. Remaining async modules (task,sync,channel,prelude) are still under conversion to preserve behaviorful/native wrappers. - Compiler: Stdlib namespace registry (
STDLIB_NAMESPACES) replaces the flat stdlib-module list — convention-based submodule discovery, registry-driven unknown-module diagnostics, and soft-keyword activation metadata. - Compiler: Reserved root namespaces —
stdandrustcannot be used as user declaration names. - Compiler: Stdlib types require explicit imports with actionable error messages when used without import.
Rust interop, libraries, and project model¶
- Compiler:
from rust import <crate>syntax for importing Rust crates — the compiler validates imports against[rust-dependencies]inincan.tomland emits the correctusestatements in generated Rust (RFC 013). - Compiler:
rust::paths accept segments whose spellings match Incan keywords (for example Rust modules namedtype), andfrom rust::... importcan import such symbols — useasfor a safe local name (e.g.from rust::my_crate::proto import type as proto_type) (#197). - Compiler:
rust-inspectand generated interop metadata are now handled much more robustly. Inspection is caller-owned again instead of ambient inTypeChecker, project-local metadata is cached undertarget/incan_lock, borrowed Rust free-function contracts are preserved further through the pipeline, and rust-analyzer-degraded borrowed pointee types such as&?are recovered from source more reliably during interop analysis (#257, #259). - Tooling:
incan testnow avoids a large amount of repeated harness work. The generatedtarget/incan_lockworkspace is fingerprinted and reused when inputs match, test discovery/runtime metadata is cached across equivalent cases, and all collected tests from a single.incnfile now run through one generated Cargo test crate instead of repeated per-test invocations (#268, #269, #271). - Compiler:
rusttypebacking types may use Rust-style qualified paths after arust::import binding (e.g.type Binary = rusttype proto_type::Binarywhenproto_typeis an imported module). Generics on qualified paths (a::B[T]) are rejected at parse time with a clear diagnostic (#197). - Tooling:
make buildin the Incan repository symlinks~/.cargo/bin/incanand~/.cargo/bin/incan-lspto the workspacetarget/debugbinaries when not running under CI (opt out withINCAN_SKIP_CARGO_BIN_LINK=1), so the CLI and language server on your PATH match the checkout without a separatecargo install. - Compiler: Rust crate dependency management with inline version annotations, manifest-based configuration, version validation (semver), and lock files for reproducible builds (RFC 013).
- Compiler: RFC 041 first-class Rust interop authoring surface:
rusttypedeclarations,interop:edge blocks, andstd.rustcapability markers used in genericwithbounds (RFC 041, #175). - Compiler/LSP: Imported prost
oneofpayloads now keep their concrete field/variant typing through CLI and editor analysis, so matches likeSome(RelType.Read(read))on imported Rust-backed fields resolvereadreliably instead of degrading to placeholderT(#218). - Compiler: Empty list literals now preserve destination element typing in typed call, assignment, and test-runner codegen paths, avoiding generated Rust inference failures for cases like
List[str]parameters receiving[](#229). - Compiler:
List[Self]method returns now accept list literals containing explicit instances of the enclosing class when those constructor arguments determine the same owner type, instead of degrading toList[Owner[?]]mismatches (#230). - Compiler: Generic model and class field access now resolves declared fields through the concrete owner instantiation, so expressions like
boxed.valuetypecheck correctly for receivers such asBoxed[T]instead of reporting a false missing-field error (#231). - Tooling: LSP document symbols, hover, and completions now include
newtype/rusttypeandtypealias declarations. Hovering over arusttypeshows the canonical Rust path of the underlying type (RFC 041). - Compiler: Convention-based source root resolution — the compiler and test runner resolve user module imports against
src/(or an explicit[build] source-root) so imports are uniform across source and test files (RFC 015). - Web: Compiler-to-stdlib routing handoff via
incan_stdlibmacros (RFC 022). - Web: Web-free compiler — all web-specific knowledge (route registration, extractor types, response builders) lives in
std.webstdlib modules and theincan_web_macrosproc-macro crate. The compiler uses generic decorator passthrough (@rust.extern+rust.module()) and derive passthrough (@derive(X)fromrust.module()traits) with no domain-specific web logic in the compiler core (RFC 023). - Web: Import-driven web feature activation — importing
std.webautomatically enablesaxum,incan_web_macros, andinventorydependencies in the generated project (RFC 023). - Runtime:
incan_stdlib::r#asyncnow provides the narrow Rust runtime leaves behind the source-declaredstd.asyncmodules, rather than exposing the async surface as a raw Tokio re-export facade. - Tooling:
incan initscaffolds a full project:src/main.incn,tests/test_main.incn, andincan.tomlwith[project.scripts] mainset (RFC 015). - Tooling:
incan lockgeneratesincan.lockfor reproducible builds;--lockedand--frozenflags enforce lock freshness in CI (RFC 013). - Tooling: Test runner resolves imports against the project source root, enabling tests to import directly from source modules (e.g.,
from greet import greet) without duplication (RFC 015). - Tooling: Test marker semantics (
@skip,@xfail,@slow,@fixture,@parametrize) are derived fromstd.testingmetadata instead of brittle string/path hardcoding (RFC 023). - Parser:
asyncis allowed as a module path segment in imports (e.g.,from std.async.time import sleep). - Diagnostics: Clearer error messages for unknown decorators and reserved namespace violations.
- Diagnostics: Enhanced lock-out-of-date error with expected vs actual fingerprint for easier debugging.
- Compiler:
rust::coreandrust::allocare now rejected at the typechecker level with a targeted diagnostic pointing users torust::std::...instead (reserved for futureno_std/target work) (RFC 005). - Diagnostics: Rust interop dependency errors now include the import site path and a hint to verify the crate/module/item path or add version/features in
incan.toml(RFC 005, RFC 013). - Docs: "Your first Incan project" tutorial covering
incan init, modules, and testing. - Docs: Rust interoperability how-to expanded with
rusttype,interop:adapters, capability bounds fromstd.rust, and explicit Rust-boundary coercion guidance (RFC 041, #175). The same guide now covers keyword-named Rust path segments, qualifiedrusttypebacking paths, and the LSP page recommendsmake buildfor keeping CLI andincan-lspaligned (#197). - Docs: Contributor docs now describe the registry-driven keyword path, including
incan-vocab, parserIdentkeyword handling, and generated VS Code grammar synchronization (RFC 027). - Codegen: String literals passed to external Rust type methods now emit
.into()instead of.to_string(), letting the Rust compiler resolve custom string types (e.g. Polars'PlSmallStr) via theIntotrait. - Compiler: MSRV bumped from 1.85 to 1.91 (required by the current compiler/runtime/tooling dependency set, including
wasmtime40.x and rust-analyzer metadata crates). - Compiler/Tooling: AST expression traversal for serde runtime detection and test-fixture teardown discovery is now centralized through a shared walker, closing false-negative gaps in contexts like
elifbranches and loop condition/iterator expressions (#123). - Compiler: Explicit generic
withbounds now use nominal trait conformance (including transitive supertraits and trait-typed arguments) for non-builtin trait capabilities, so capability-constrained APIs are enforced without receiver-name special-casing (#299).
Stabilization and bugfixes¶
- Compiler/Codegen: A broad ownership/materialization pass now keeps generated Rust much closer to ordinary Incan value semantics. Filtered comprehensions,
Result[str, E]payloads,clone(self) -> Selfstruct constructors, borrowed enum loops, repeated helper calls over sharedlist[str]values, andlist[str].append("...")all stopped requiring downstream ownership workarounds in real projects (#364, #366, #367, #372, #383, #391). - Parser/Compiler: Python-shaped iteration and pattern forms are now materially stronger. Qualified enum constructor patterns like
ConformanceRel.Filter =>resolve correctly inmatcharms,for idx, name in enumerate(xs):now parses and lowers correctly, andenumerate(...)emits ordinary Incanint(i64) indices instead of leaking Rustusizevalues through tuple-unpacked loop bindings (#374, #389). - Compiler/Codegen: Imported and user-defined helpers that share names with core builtins now keep their ordinary function resolution through typechecking, lowering, and Rust emission, so aggregate-builder libraries no longer need aliasing workarounds like
sum as agg_sum, and asyncsleepnow consistently follows stdlib import semantics instead of a legacy builtin side path (#377). - Tooling: Generated
incan testharnesses now reset their runtime cwd to the owning project root, so fixture paths liketests/fixtures/orders.csvresolve the same way in single-file and batched test runs (#378). - Compiler:
len(...)now lowers to a parse-safeusize -> i64expression in generated Rust comparisons, so expressions likelen(xs) < 2no longer fail emission with a Rust syntax error (#380). - Compiler:
pub from ... import ...now works in any module undersrc/(not justsrc/lib.incn), which unblocks package submodule facades that re-export symbols for parent imports likefrom session import Session(#287). - Compiler/Codegen: Multi-file generated Rust now escapes keyword-named module declarations consistently with explicit
#[path = ...]attributes, so top-level and nested modules whose names collide with Rust keywords build and run cleanly through the full pipeline (#122). - Compiler / Stdlib: Lists now support
list + listconcatenation andlist.extend(other)with Python-like source-list preservation, so combining lists no longer fails at typecheck/codegen time or consumes the original bindings unexpectedly (#181). - Tooling:
incan.tomlparsing and validation errors now include manifest line/column locations across malformed TOML, conflicting dependency sections, unknown fields, and dependency-entry shape/type errors, making migration and dependency configuration failures much easier to locate (#130). - Tooling:
incan testnow isolates requested single-file runs correctly and reuses shared build/rust-inspect metadata more aggressively across sessions, which fixes cross-wired outcomes and reduces repeated cold-start overhead in real test workflows (#288). - Compiler/Tooling:
await expr?now lowers/emits in Rust-safe order (expr.await?), andincan fmtnow preserves more source intent when round-tripping code: escaped f-string newlines stay as\\ntext, qualified enum/constructor match patterns stay in dot form, numeric literals keep their source spelling, blank-line normalization stays stable inside blocks,mutmarkers on parameters are preserved, and output ends with a single trailing newline instead of formatter-induced drift (#235, #250, #264, #289). - Compiler: Cyclic cross-module projects now resolve imported dependencies during per-module typechecking by import graph (not discovery order), so direct explicit call-site generic calls like
collect_with_active_session[T](...)no longer fail with a false unsupported-call-form diagnostic inincan run/incan --checkflows (#279). - Tooling/Codegen: Generated projects now resolve relative entry paths correctly and emit cleaner Rust for internal module calls and module statics, reducing false errors and compiler-noise in real project workflows.
- Compiler/Core: Shared language registries now resolve builtin/trait/operator metadata through exhaustive enum matches instead of panic-backed
.expect(...)lookups, tightening internal compiler invariants and guardrail coverage (#346). - Compiler: Generic instance methods on classes, traits, models, and newtypes now parse and flow through formatting, typechecking, lowering, and library export metadata correctly (#253).
- Compiler: Locals initialized from call expressions now preserve the callee's declared result type through later field access, method chaining, and
matchscrutinees. This fixes false receiver-typed errors after static factory calls such asSession.default()and the related local-inference family regressions (#252, #255). - Tooling:
incan runnow defaults to Cargo's debug profile for faster cold-start iteration, supports explicit--releasefor optimized runs, and prints clear build-phase progress before program execution so first-run compiles no longer look hung (#276). - Compiler/Codegen: Checked
newtypeconstructors no longer emit.expect(...)extraction in generated Rust. The success path is lowered as an explicitmatch, keeping emitted Rust valid while preserving the existing panic-on-invalid-construction contract. - Compiler:
pub::library consumers now preserve transitive imported method result types and trait/supertrait compatibility, so calls likelazy.collect()?keep theirResult[...]contract andDataFrame[T]values satisfyDataSet[T]parameters across library boundaries (#277, #278). - Parser/formatter: Leading
"""..."""docstrings at the start ofmodel,class,enum,trait, andnewtypebodies are stored on the AST and round-trip throughincan fmtinstead of being discarded. Public (pub) type-like declarations expose that doc text to library tooling viaexported_type_like_docs(#247). - Compiler: Method dispatch on generic classes and models now substitutes
Selfin formal parameters and return types using the instantiated receiver at the call site, so carriers likeLazyFrame[Order]no longer mis-type chained calls as bareSelf(#237). - Compiler: Call-site codegen no longer applies Incan’s generic
.clone()materialization to mutable aggregate parameters (lists, dicts, and similar) that are lowered as&mutin Rust, fixing rustcE0308mismatches (expected &mut Vec,found Vec) for recursive or loop-nested calls that re-use the samemutcollections (#244). - Compiler: Receiver-sensitive known-method lowering prevents non-string
.join(...)calls from mis-emitting asincan_stdlib::strings::str_join(#236). - Compiler:
matchonrusttype-wrapped Rust enums and oneof-shaped values now binds variant payload names inside the arm body (same scoping rules as Incan enums), fixing unknown-symbol errors when inspecting imported protobuf-style types (RFC 005, RFC 041, #217). - Compiler:
awaitis rejected when it appears outside anasync defor async method body, matching async/await scoping rules (#210). - Compiler:
for ... inover a variable-boundlistof enums now iterates with owned enum values in generated Rust, so comparisons such asitem == some_enumcompile without comparing&EtoE(#195). - Compiler:
list.pop()no longer lowers toVec::pop().unwrap_or_default(), so element types do not needDefault(e.g. Clone-only models). Popping an empty list now raisesIndexError: pop from empty list, matching Python (#194). - Compiler: Types with
@derive(Clone)now typecheck direct.clone()on concrete receivers (models and enums), matching behavior already allowed through unconstrained generics; builtin trait vocabulary stubs load method signatures from stdlib when needed (#193). - Diagnostics: Unknown symbol errors in f-string interpolations now point to the precise
{expr}span (including nested interpolation expressions) instead of the full f-string (RFC 011, #71). - Compiler: Fixed project root resolving to empty string when running
incan run src/main.incnfrom the project directory, causing "No such file or directory" errors. - Compiler: Trait-typed values are now correctly assignable to supertrait return types in function returns and generic upcasts with compatible type arguments (RFC 042, #184).
- Docs: Tutorial/reference examples were updated to match the new
0.2surface, includingstd.webimports, namespaced decorators, and@rust.externusage. - Compiler: Trait bound inference now covers trait methods and impl methods, not just standalone functions. Generic bounds required by return types (e.g.,
impl BoundedDataSet<T>) are properly inferred and emitted in the Rust codegen, even when the bounds aren't used in the function body (#196). - Compiler/Tooling: Library builds now treat
mod.incnas a directory entrypoint instead of a semantic module segment, so nested package modules no longer leakcrate::<module>::r#modpaths or stale flat-vs-nested Rust files duringincan build --lib(#227).
Known limitations (0.2)¶
- Web route handlers and their request models must be declared
pubfor cross-module wrapper generation (#117). - Rust macro syntax (e.g.,
serde_json::json!(...)) is not supported in Incan source — use Rust crate functions and types directly instead. incan fmtremains intentionally conservative and can still leave ordinary signatures, constructor calls, and nested expressions beyond the documented 120-character line-length target (#248). The broader formatter contract work, including vertical spacing rules, is tracked separately in RFC 053 / #336.- Compiler: Ownership/borrow lowering is still handled by a set of compiler heuristics rather than a complete, centralized ownership planner.
0.2fixed the known release-blocking cases, but the broader duckborrowing design is tracked for0.3in #121; field-backed method-call.clone()workarounds remain one visible symptom (#241).
RFCs implemented¶
- First-class named function references and
Callablesugar: RFC 035 - Namespaced stdlib modules and compiler-to-stdlib handoff: RFC 022
- Compilable stdlib and Rust module binding (web-free compiler): RFC 023
- Library vocabularies, registry-driven keyword activation, and vocab desugaring: RFC 027
- Module static storage: RFC 052
- Rust crate dependency management: RFC 013
- Hatch-like tooling (partial:
incan init, project discovery, source root resolution): RFC 015 - Rust interop surface (
rust::imports, type mapping, string borrow adaptation, curated derives): RFC 005 - Precise f-string interpolation diagnostics: RFC 011
- Trait supertrait assignability and generic upcasts: RFC 042
- Trait bound inference for trait/impl methods and return types: RFC 023
- Explicit call-site generic arguments (including
_inference placeholders): RFC 054