Strings and ByteArrays
In Cairo, there's no native type for strings. Instead, you can use a single felt252
to store a short string
or a ByteArray
for strings of arbitrary length.
Short strings
Each character is encoded on 8 bits following the ASCII standard, so it's possible to store up to 31 characters in a single felt252
.
Short strings are declared with single quotes, like this: 'Hello, World!'
.
See the Felt section for more information about short strings with the felt252
type.
Notice that any short string only use up to 31 bytes, so it's possible to represent any short string with
bytes31
.
ByteArray (Long strings)
The ByteArray
struct is used to store strings of arbitrary length. It contains a field data
of type Array<bytes31>
to store a sequence of short strings.
ByteArrays are declared with double quotes, like this: "Hello, World!"
.
They can be stored in the contract's storage and passed as arguments to entrypoints.
#[starknet::interface]
pub trait IMessage<TContractState> {
fn append(ref self: TContractState, str: ByteArray);
fn prepend(ref self: TContractState, str: ByteArray);
}
#[starknet::contract]
pub mod MessageContract {
#[storage]
struct Storage {
message: ByteArray
}
#[constructor]
fn constructor(ref self: ContractState) {
self.message.write("World!");
}
#[abi(embed_v0)]
impl MessageContract of super::IMessage<ContractState> {
fn append(ref self: ContractState, str: ByteArray) {
self.message.write(self.message.read() + str);
}
fn prepend(ref self: ContractState, str: ByteArray) {
self.message.write(str + self.message.read());
}
}
}
#[cfg(test)]
mod tests {
use bytearray::bytearray::{
MessageContract::messageContractMemberStateTrait, MessageContract, IMessage
};
#[test]
#[available_gas(2000000000)]
fn message_contract_tests() {
let mut state = MessageContract::contract_state_for_testing();
state.message.write("World!");
let message = state.message.read();
assert(message == "World!", 'wrong message');
state.append(" Good day, sir!");
assert(state.message.read() == "World! Good day, sir!", 'wrong message (append)');
state.prepend("Hello, ");
assert(state.message.read() == "Hello, World! Good day, sir!", 'wrong message (prepend)');
}
}
Operations
ByteArrays also provide a set of operations that facilitate the manipulation of strings.
Here are the available operations on an instance of ByteArray
:
append(mut other: @ByteArray)
- Append another ByteArray to the current one.append_word(word: felt252, len: usize)
- Append a short string to the ByteArray. You need to ensure thatlen
is at most 31 and thatword
can be converted to abytes31
with maximumlen
bytes/characters.append_byte(byte: felt252)
- Append a single byte/character to the end of the ByteArray.len() -> usize
- Get the length of the ByteArray.at(index: usize) -> Option<u8>
- Access the character at the given index.rev() -> ByteArray
- Return a new ByteArray with the characters of the original one in reverse order.append_word_rev(word: felt252, len: usize)
- Append a short string to the ByteArray in reverse order. You need to ensure again thatlen
is at most 31 and thatword
can be converted to abytes31
with maximumlen
bytes/characters.
Additionally, there are some operations that can be called as static functions:
concat(left: @ByteArray, right: @ByteArray)
- Concatenate two ByteArrays.
Concatenation of ByteArray
(append(mut other: @ByteArray)
) can also be done with the +
and +=
operators directly, and access to a specific index can be done with the []
operator (with the maximum index being len() - 1
).