Sway Dilinin Temel Kavramları
Sway programlama dili Fuel sanal makinesi (FuelVM) için geliştirilmiştir ve syntax (söz dizimi) olarak Rust'a çok benzer. Yapısal ve işlevsel olarak da Rust'a çok benzer ancak Solidity'den de ilham alınan yönleri mevcuttur. Güvenli ve performanslı bir akıllı kontrat geliştirme olanağı sağlamak amacıyla geliştirilmiştir.
Değişkenler
Sway'de değişken atamaları let
anahtar sözcüğü ile yapılır. Değişkenler varsayılan (default) olarak immutable
yani değiştirilemezdir.
let my_var = 8;
Yukarıda değeri 8'e eşit olan "my_var" adında bir tam sayı (integer) tanımladık. Tam sayılarda eğer tip ataması yapılmazsa varsayılan olarak u64
olarak atanırlar.
let mut my_var: u32 = 8;
my_var = 10;
Yukarıda aynı değişkenin tip atamasını manuel olarak u32
atadık vemut
anahtar sözcüğü ile değiştirilebilir (mutable) kılıp değerini 10'a eşitledik.
Sabitler (Constants)
Sway'de de tıpkı Rust'ta olduğu gibi sabit değerli (constant) değerler atanabilir. Bunun için Forc.toml
adlı manifest dosyasında ilgili bölümde tanımlanıp
kullanılabilir hale gelirler.
[constants]
my_contract_address = { type= "b256", value="0x373746fjfkgktkbkfkkg499r9r94eıgkrg9e09t9ıg" }
my_variable = { type="u64", value="61" }
my_bool = { type="bool", value="true" }
Yukarıda görüldüğü gibi bir constant tanımlamak için iki şey gereklidir: birincisi o sabitin type
yani tipi, ikincisi de o sabitin değeri yani value
.
Tanımlanan sabitler artık tanımlandığı Forc.toml
dosyasını manifest dosyası olarak alan programda kullanılabilir hale gelirler:
script;
fn main() {
let my_addr = my_contract_address;
return if my_bool { my_variable } else { 28 };
}
Yukarıdaki kod blokunda görüldüğü gibi manifest dosyasında tanımlanan sabitler herhangi bir import (çağırma) işlemi yapılmadan doğrudan programda kullanıldı. Sabitler şimdilik sadece primitive (ilkel) tipler için tanımlanabilir olsa da gelecekte yapılacak güncellemelerle değişikliğe uğrayacak.
Primitive (İlkel) Tipler
Sway programlama dilinde her değerin mutlaka bir tipi olmak zorundadır, derleme zamanında Sway bu değerlerin tiplerinin ne olduğunu bilmek ister. manuel olarak tip ataması yapılmamışsa derleyici bir çıkarım yaparak varsayılan tip atamasını o değere atayacaktır.
Sway yedi farklı ilkel tipe sahiptir, bunlar:
- u8 (8-bit işaretsiz tam sayı)
- u16 (16-bit işaretsiz tam sayı)
- u32 (32-bit işaretsiz tam sayı)
- u64 (64-bit işaretsiz tam sayı)
- str[] (sabit uzunluklu string yani dize)
- bool (Boolean (mantıksal) true ya da false)
- b256 (256 bit (32 bayt) yani hash)
Yukarıdaki tipler Sway kütüphanesindeki yerleşik (built-in) tiplerdir. Tasarımsal olarak Sway'de Rust'ın aksine işaretli (signed) ve kayan noktalı (floating-point) tipler tanımlanmamıştır çünkü bunların kullanım alanı azdır, bu sebeple spesifik kullanımlar için uygulanması dış kütüphanelere bırakılmıştır.
İşaretsiz tam sayılarda (unsigned integer) varsayılan tip u64
olarak tanımlanmıştır ve ikilik (binary), altılık (hexadecimal) ve onluk (base-10) gibi farklı tabanlarda ataması yapılabilir:
0xffffff // altılık (hexadecimal)
0b10101010 // ikilik (binary)
10 // onluk (base-10)
Ayrıca okunurluk açısından bu tür numerik sayılar alt çizgi -
ile ayrılarak da gösterilebilir, derleyici bu alt çizgileri görmezden gelir:
100_000
0x1111_0000
0xfff_aaa
Boolean yani mantıksal tip değişkenler iki farklı değer alabilir, bunlar true
ve false
. Ayrıca ünlem işareti (!) ile bu iki değer birbiri
arasında dönüştürülebilir. Aşağıda true olarak atanan bir değer ünlem işareti kullanılarak false yapılıp fonksiyondan geri döndürülüyor:
fn false_conversion() -> bool {
let my_val: bool = true;
!my_val
}
String yani dize tipi Sway'de statik uzunluklu olarak tanımlanmıştır, boyutu aynı zamanda onun tipinin bir ögesidir. Derleme zamanında derleyicinin bellekte ne kadar yer ayıracağını bilmesi açısından bu gereklidir. Aşağıdaki kodda görüldüğü şekilde tanımlanırlar:
let my_val: str[4] = "sway";
Yukarıdaki "my_val" adlı değişkenin değeri olan "sway" 4 harfli olduğu için tipi str[4]
olarak tanımlanır.
Bileşik (compound) tipler
Bileşik tipler birden fazla değere sahip olan ve bu değerlerin tek bir tipte tanımlandığı tiplerdir, Sway iki farklı bileşik tipe sahiptir:
- Tuple (demet)
- Array (dizi)
Tuple, bir veya birden fazla aynı veya farklı türde değerlerin tek tipte toplanmasıyla oluşturulur.
let my_val: (u64, u64) = (28,61);
Yukarıda bir tuple tanımlanmış, tuple değerleri ve tip ataması parantez () içerisinde yazılır. Tip atamaları yapılırken değerlerin sırasına dikkat edilmelidir. Aşağıda farklı tiplere sahip bir tuple tanımlaması yapılmış ve tipleri değerlerinin sırasına göre belirtilmiş:
let x: (u32, bool) = (34, true);
let first = x.0;
let second = x.1;
Yukarıda x değişkeni (u32, bool) şeklinde iki farklı alt tipten oluşan bir tuple. Tuple elemanlarına erişmek için indeksleme yöntemi kullanılır.
Bunun için sıfırdan başlanarak indeks numarasına göre sırayla tuple değerlerine erişilebilir, yani x.0
değeri "34" ve x.1
değeri ise "true" olur.
Ayrıca "destructuring" yani parçalama yöntemi ile de tuple değerlerine erişilebilir:
let x: (u32, u64, bool) = (30, 12, true);
let (a, b, c) = x;
let y: (u64,) = (36,);
Yukarıda a, b ve c değişkenlerinin değeri destructuring yöntemiyle parçalanarak sırasıyla 30, 12 ve true olarak atanır. Son satırdaki "y" değişkeninde ise tek bir değeri olan tuple tanımlanmış, yani eğer tek değeri olan bir tuple oluşturmak istenirse hem tip ataması hem de değeri yazılırken virgül konulmalıdır.
Array yani diziler de tuple gibidir ancak farklı olarak dizilerin bütün değerleri aynı tipten oluşmalıdır. Aşağıdaki gibi köşeli parantez [] içerisinde değerleri virgülle ayrılarak oluşturulurlar.
let my_array: [u64; 6] = [1, 2, 3, 4, 5, 6];
Sway'de diziler belleğin stack
adlı bölümünde tutulurlar çünkü boyutları her zaman sabittir, bir kere tanımlandıktan sonra boyutu değiştirilemez, yeni elemanlar
eklenemez veya çıkartılamaz. Tip ataması yapılırken köşeli parantez içerisine ilk önce dizi elemanlarının tipi yazılır ikinci olarak da eleman sayısı noktalı virgül (;)
ile ayrılarak yazılır. Yukarıdaki "my_array" adlı dizinin tipi [u64; 6]
olarak tanımlanmış.
let mut my_array: [u64; 6] = [1, 2, 3, 4, 5, 6];
let first = my_array[0];
my_array[1] = 9;
Dizi elemanlarına erişim yine indeksleme yöntemiyle ve köşeli parantez kullanılarak yapılır. Yukarıdaki dizinin ilk elemanına erişmek için my_array[0]
yazılır.
Eğer dizi mut
anahtar sözcüğü ile değiştirilebilir kılınmışsa dizi elemanlarının değeri indeksleme ile değiştirilebilir. Yukarıdaki kod blokunda
dizi tanımlanırken mut
ile değiştirilebilir kılındığından birinci indeksteki elemanının değeri my_array[1] = 9;
ifadesi ile 9' a eşitlenmiş.
Sıklıkla kullanılan Sway tipleri
Sway Standard Kütüphanesi içerisinde belirli amaçlar ve konseptler için geliştirilmiş birden çok modül bulunmaktadır. Bunların içerisinde yer alan özelleştirilmiş iki farklı tipi ele alacağız, bunlar:
Result<T,E>
Option<T>
Result<T,E>
Result
tipi iki farklı değerden oluşan bir enum'dır, ilk değeri olan Ok(T)
eğer başarılı bir sonuç döndürülürse çalışır ve içerisindeki T
değerini döndürür,
ikinci değeri ise Err(E)
dir ve hata alınması durumunda içerisindeki E
değerini döndürür. Bu enum tıpkı Rust'ta olduğu gibi hata ile karşılaşılabilecek
durumlarda işleyip geriye anlamlı bir sonuç döndürmek için kullanılır.
pub enum Result<T, E> {
Ok: T,
Err: E,
}
Aşağıdaki örnekte multiply
adlı fonksiyon iki değer alıp bunların çarpımını geriye döndürüyor, ancak aldığı parametrelerden ikisi de sıfıra eşitse hata olacağından
bu hatayı işlemek için Result
kullanıp dönen değeri hata ya da başarılı olma durumuna göre ele alıyoruz.
script;
enum HandleError {
MultiplyTwoZero: (),
}
fn multiply(x: u64, y: u64) -> Result<u64, HandleError> {
if (x == 0 && y == 0) {
return Result::Err(HandleError::MultiplyTwoZero);
} else {
Result::Ok(x * y)
}
}
fn main() -> Result<u64, str[6]> {
let result = multiply(20, 2);
match result {
Result::Ok(val) => Result::Ok(val),
Result::Err(HandleError::MultiplyTwoZero) => Result::Err("Failed"),
}
}
Option<T>
Option
tipi iki farklı ögesi olan bir enum'dır, birinci ögesi olan Some
bir değeri temsil eder ve içerisindeki değer ne ise geriye onu döndürür, ikinci ögesi
olan None
ise hiçbir değerin döndürülmemesi durumunu sembolize eder. Özellikle bir değişkene başlangıç değeri atamak ve hata işleme durumlarında kullanılırlar.
pub enum Option<T> {
None: (),
Some: T,
}
Aşağıdaki örnekte divide
adlı fonksiyon iki parametre alarak bölme işlemi yapıp Option ile geriye döndürüyor, eğer hatalı bir işlem olursa Option
içerisindeki None
değerini döndürüyor, başarılı bir işlem olursa da sonucunu Some
ile döndürüyor.
script;
fn divide(numerator: u64, denominator: u64) -> Option<u64> {
if denominator == 0 {
Option::None
} else {
Option::Some(numerator / denominator)
}
}
fn main() {
let result = divide(9, 3);
match result {
Option::Some(x) => std::logging::log(x),
Option::None => std::logging::log("Cannot divide by 0"),
}
}
Blokzincir tipleri
Sway programlama dili blokzincirine has bir dil olduğundan, standart kütüphanesinde bu alan için özelleştirilmiş birtakım tipler de içermektedir. Bunlar:
- Address
- ContractId
- Identity
Address
Address
tipi primitive (ilkel) tiplerden biri olan b256
üzerinde güvenli bir sarmalayıcı olarak işlev görür, bir public anahtarın ya da bir predicate'in
hash değerini ifade eder ve deploy edilmiş (yayımlanmış) bir akıllı sözleşmeye referans olamazlar.
pub struct Address {
value: b256,
}
b256
ve Address
tipleri arasında tip dönüşümü (casting) yapılabilir:
let my_num: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
let my_addr: Address = Address::from(my_num);
let forty_two: b256 = my_addr.into();
ContractId
ContractId
tipi primitive (ilkel) tiplerden biri olan b256
üzerinde güvenli bir sarmalayıcı olarak işlev görür, EVM'deki kontrat adreslerine benzeyen
bir tanımlayıcı (id) işlevi görürler.
pub struct ContractId {
value: b256,
}
b256
ve ContractId
arasında tip dönüşümleri (casting) aşağıdaki gibi yapılabilir:
let my_number: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A;
let my_contract_id: ContractId = ContractId::from(my_number);
let forty_two: b256 = my_contract_id.into();
Identity
Identity
tipi hem Address
hem de ContractId
tiplerini bir arada ele alan bir enum'dır, iki tipin de kabul edilebilir olduğu durumlarda kullanışlı olabilir.
Örneğin kimliği belli bir göndericiden gelen fonları alırken göndericinin bir adres ya da kontrat olup olmadığının önemi olmayan durumlar gibi.
pub enum Identity {
Address: Address,
ContractId: ContractId,
}
Identity
için tip dönüşümleri aşağıdaki gibi yapılabilir:
let raw_address: b256 = 0xddec0e7e6a9a4a4e3e57d08d080d71a299c628a46bc609aab4627695679421ca;
let my_identity: Identity = Identity::Address(Address::from(raw_address));
Bir Identity
içerisindeki Address
ya da ContractId
yi döndürmek için match
ifadesi kullanılabilir:
let my_contract_id: ContractId = match my_identity {
Identity::ContractId(identity) => identity,
_ => revert(0),
};