uCalc API Version: 5.7.0-preview.1 Released: 7/30/2026

Warning

uCalc API Preview Release Notice:This preview documentation describes the intended behavior of the API. It is not fully accurate or complete.The current preview build contains incomplete features, unoptimized performance, and is subject to breaking changes.Use of the preview version in your production code is not recommended.

Common Gotchas

Product: 

Class: 

A guide to the most common mistakes and misunderstandings developers encounter when using uCalc, with solutions and best practices.

Remarks

⚠️ Common uCalc Gotchas & Pitfalls

This guide covers the most frequent mistakes developers make when working with uCalc. Understanding these points will save you significant debugging time and help you write more efficient, robust code.


1. 🐌 Performance: Using Eval() Inside a Loop

The Pitfall: Calling Eval() or EvalStr() inside a high-frequency loop.

Why it's a problem: Every call to Eval() performs two steps: parsing the string into an executable plan (slow) and then evaluating that plan (fast). In a loop, you're repeating the slow parsing step unnecessarily for every iteration.

The Solution: Use the "Parse-Once, Evaluate-Many" pattern.

csharp
// Correct, High-Performance Way
var x_var = uc.DefineVariable("x");
// 1. Parse ONCE, outside the loop.
var expr = uc.Parse("x * x + 2");

// 2. Evaluate MANY times, inside the loop. This is very fast.
for ( i = 1; i <= 1000; i++) {
x_var.Value(i);
Console.WriteLine(expr.Evaluate());
}
For more details, see the Optimizing Performance tutorial.


2. 🧠 Transformer Rules: Precedence is LIFO

The Pitfall: Assuming transformer rules are applied in the order they are defined.

Why it's a problem: uCalc uses a LIFO (Last-In, First-Out) stack for rule precedence. The last rule defined is checked first. This is often counter-intuitive.

**The Solution**: Always define your **most specific rules last** to give them the highest priority.```csharp
// Correct Way
var t = new uCalc.Transformer();
t.FromTo("An {item}", "ITEM"); // General rule first
t.FromTo("An apple", "FRUIT"); // Specific rule last

// This will correctly output "FRUIT".
Console.WriteLine(t.Transform("An apple"));
```---## 3. 💧 Memory Management: Forgetting to `Release()`**The Pitfall**: Not releasing uCalc objects in non-RAII environments.**Why it's a problem**: The uCalc core is C++. In managed languages like C#, the garbage collector only cleans up the .NET handle, not the underlying C++ engine instance. This causes a **memory leak**.**The Solution**: Use language-idiomatic resource management.* **C# / VB.NET**: Always wrap object creation in a `using` block.* **C++**: Use stack-allocated objects and call `Owned()` to enable RAII.* **Manual**: If an object's lifetime is not scoped, you **must** call [Release()](/Reference/uCalcBase/uCalc/Release) when you are done with it.```csharp

// Correct C# Way
using (var t = uc.NewTransformer())
{
// ... use t ...
} // t.Release() is called automatically here.


```See the [Memory Management](/Concepts/Memory-Management) guide for more information.---## 4. 🔢 Types in `{@Eval}`: Forgetting String Conversion**The Pitfall**: Trying to perform math on a captured variable inside an [{@Eval}](/Reference/Patterns/Pattern-Methods/{@Eval}) block without converting it to a number.**Why it's a problem**: All variables captured from a pattern, even from `{@Number:val}`, are **strings**. An expression like `val * 2` will result in string concatenation, not multiplication.**The Solution**: Explicitly cast the string to a numeric type using functions like `Double()` or `Int()`.```csharp
var t = new uCalc.Transformer();
// Incorrect: "10" * 2 would result in repeating the string twice resulting in "1010".
// Correct: Use Double() to convert the string '10' to a number.
t.FromTo("{@Number:val}", "{@Eval: Double(val) * 2}");

Console.WriteLine(t.Transform("Value: 10"));
```For details, see [Executing Logic in Replacements](/Tutorials/Text-Transformation:-The-Transformer/Executing-Logic-in-Replacements).---## 5. 🔀 Alternation Order: Longest Match First**The Pitfall**: In an [alternation](/Reference/Patterns/Introduction/Alternation) `|`, placing a shorter, partial match before a longer, more specific one.**Why it's a problem**: The engine checks alternatives from **left to right** and stops at the first successful match.```csharp
// Incorrect Order
var t = new uCalc.Transformer();
// "Apple" will match first, so "Apple Pie" is never checked.
t.FromTo("{ Apple | Apple Pie }", "MATCH");

Console.WriteLine(t.Transform("An Apple Pie")); // Incorrectly matches only "Apple"
```**The Solution**: Always place the longest, most specific patterns first in an alternation.```csharp
// Correct Order
var t = new uCalc.Transformer();
t.FromTo("{ Apple Pie | Apple }", "MATCH");

Console.WriteLine(t.Transform("An Apple Pie")); // Correctly matches "Apple Pie"
```---## 6. 📝 Indexing: 1-based vs. 0-based**The Pitfall**: Confusing 1-based and 0-based indexing.**Why it's a problem**: Different parts of the uCalc API use different conventions.* **1-based**: Arguments in a [Callback](/Reference/uCalcBase/Callback/Constructor) (e.g., `cb.Arg(1)`).* **0-based**: Most collections, like [Matches](/Reference/uCalcBase/Matches/Constructor) (`myMatches[0]`) and [Tokens](/Reference/uCalcBase/Tokens/Constructor) (`myTokens.At(0)`).**The Solution**: Be mindful of the context. When in a callback interacting with arguments, think 1-based. When iterating a collection of results, think 0-based.

Examples