Before porting a text limit, establish what the number counts. The same string can have different byte, UTF-16 code-unit and Unicode code-point counts. None should automatically be treated as a count of characters visible on screen.
A text-length mismatch appeared while building a separate Rust edition intended to preserve existing features. The original application itself used more than one unit: its per-item validator and its explicit aggregate expression counted length differently.
Count the same input in different units
In an independent probe for this article, I recorded Rust’s byte length, the scalar-value count from chars, and the code-unit count from encode_utf16. In Node I recorded UTF-8 bytes, the count from spreading the string, and its length property, then compared the same inputs. This did not run the original browser or its product tests.
Korean text and an emoji were enough to expose the difference. These are measured results from this run’s synthetic strings, not measurements of rendered width or visible character clusters. For these well-formed Unicode inputs, Rust’s scalar count matched the code-point count obtained in Node.
Synthetic measurements, in this order: UTF-8 bytes / Unicode scalar values corresponding to these code points / UTF-16 units.
Explore the implementation
Synthetic string measurements; not a rendered character-count test
a : 1 / 1 / 1
가 : 3 / 1 / 1
😀 : 4 / 1 / 2
e + U+0301 : 3 / 2 / 2
👩 + U+200D + 💻 : 11 / 3 / 5Inspect the actual validator before choosing a language default
The inspected application had Zod 4.6.7 installed. Its string min/max implementation checked code-point length. Assuming that the library uses UTF-16 units merely because JavaScript string length does so could reject inputs that the original application accepted.
I used a synthetic maximum of 2, separate from the product’s real limits. The installed library accepted 1 and 2 emoji and rejected 3. The JavaScript length of 2 emoji was 4, yet the validator accepted the string. The result distinguished the validator’s rule from the language’s length property.
The Rust source inspected for this article compares this string limit with chars().count(). The work record describes correcting an earlier rule that rejected emoji accepted by the original. I did not rerun those product tests. I preserved the installed version and source scope, then compared the acceptance boundary with independent synthetic inputs.
Per-item limits and aggregate limits were different checks
In the original notebook code, the library validates each cell’s source, while a separate expression adds source.length across all cells. The per-item rule therefore counts code points, and the aggregate counts UTF-16 units. Two checks of the same text need not share a counting unit.
The inspected Rust code keeps both calculations. Individual limits use scalar counts; the aggregate sums encode_utf16().count(). Replacing them with one length helper could change which inputs are accepted. Unifying the units needs review as a separate behavioural change.
The independent example used a per-item maximum of 2 and a total maximum of 4 UTF-16 units. With one emoji per cell, 2 cells passed at a total of 4; 3 cells passed every per-item check but failed at a total of 6. The Rust and Node probes agreed. These small limits are synthetic, not product settings.
Record the unit and boundary beside each check
Before a port, record the input name, checking expression, unit, boundary and normalization order together. “Maximum characters” alone makes it difficult to locate a byte conversion or a library rule that differs from the application’s own expression.
Keep the actual validator and version in that comparison. Check another version or function before assuming it follows the same rule. Matching string lengths also does not establish compatibility for sorting, URL interpretation or other unrelated behaviour.
- Storage or transport size: identify the encoding whose bytes are counted.
- Validator string limits: record the installed version and acceptance boundary.
- Explicit aggregate expressions: check whether they use a different unit from per-item validation.
- Visible character clusters and wrapping: separately check font, width and segmentation requirements.
Code points are still separate from visible character clusters
The synthetic inputs included e followed by a combining mark and an emoji sequence containing a joiner. I recorded scalar and UTF-16 counts without using them to decide how many characters a reader sees. Counting code points does not establish rendered width or cluster behaviour.
A product that limits user-perceived character clusters needs that requirement defined separately. Its segmentation rule, editing behaviour and wrapping need appropriate screen checks. Mixing exact compatibility with a new input policy makes it harder to identify which change altered acceptance.
This probe covers well-formed Unicode strings. It did not test lone surrogates, normalization policy, browser rendering or input-method composition. Record which counts matched, without claiming that all character handling has been verified.
Match the evidence to the dated source and the new tests
This run compared counts for 5 synthetic strings and 5 acceptance-boundary cases. Node reused the installed validation library; an independent Rust program evaluated the corresponding scalar counts, UTF-16 counts and boundary expressions. Agreement on these examples does not prove feature parity for the original application.
At the time of inspection, the original Rust migration and screen review remained with their assigned owner. This writing work did not modify that source or open work windows, logins or tabs. The synthetic results above and the historical product tests should each be read within their recorded date and scope.