diff --git a/Cargo.lock b/Cargo.lock
index ee62af3..104f336 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -197,6 +197,7 @@ dependencies = [
  "serde",
  "serde_json",
  "smallvec",
+ "smartstring",
 ]
 
 [[package]]
@@ -496,6 +497,18 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "smartstring"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
+dependencies = [
+ "autocfg",
+ "serde",
+ "static_assertions",
+ "version_check",
+]
+
 [[package]]
 name = "spin"
 version = "0.9.8"
@@ -511,6 +524,12 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
 
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
 [[package]]
 name = "strsim"
 version = "0.11.1"
@@ -546,6 +565,12 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
 [[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml
index e983b81..09ca4f6 100644
--- a/dust-lang/Cargo.toml
+++ b/dust-lang/Cargo.toml
@@ -19,6 +19,7 @@ getrandom = { version = "0.2", features = [
     "js",
 ] } # Indirect dependency, for wasm builds
 smallvec = { version = "1.13.2", features = ["serde"] }
+smartstring = { version = "1.0.1", features = ["serde"], default-features = false }
 
 [dev-dependencies]
 env_logger = "0.11.5"
diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs
index add03c5..b32c1ad 100644
--- a/dust-lang/src/chunk.rs
+++ b/dust-lang/src/chunk.rs
@@ -8,6 +8,7 @@ use std::fmt::{self, Debug, Display, Write};
 
 use serde::{Deserialize, Serialize};
 use smallvec::SmallVec;
+use smartstring::alias::String;
 
 use crate::{ConcreteValue, Disassembler, FunctionType, Instruction, Scope, Span, Type};
 
diff --git a/dust-lang/src/compiler.rs b/dust-lang/src/compiler.rs
index 8684de9..4f95d1c 100644
--- a/dust-lang/src/compiler.rs
+++ b/dust-lang/src/compiler.rs
@@ -18,11 +18,9 @@ use crate::{
         Call, CallNative, Close, DefineLocal, GetLocal, Jump, LoadBoolean, LoadConstant, LoadList,
         LoadSelf, Move, Negate, Not, Return, SetLocal, Test,
     },
-    optimize_control_flow, optimize_set_local,
-    value::ConcreteValue,
-    AnnotatedError, Argument, Chunk, Destination, DustError, FunctionType, Instruction, LexError,
-    Lexer, Local, NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type,
-    TypeConflict,
+    optimize_control_flow, optimize_set_local, AnnotatedError, Argument, Chunk, ConcreteValue,
+    Destination, DustError, DustString, FunctionType, Instruction, LexError, Lexer, Local,
+    NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type, TypeConflict,
 };
 
 /// Compiles the input and returns a chunk.
@@ -55,7 +53,7 @@ pub fn compile(source: &str) -> Result<Chunk, DustError> {
 /// See the [`compile`] function an example of how to create and use a Compiler.
 #[derive(Debug)]
 pub struct Compiler<'src> {
-    self_name: Option<String>,
+    self_name: Option<DustString>,
     instructions: Vec<(Instruction, Type, Span)>,
     constants: Vec<ConcreteValue>,
     locals: Vec<Local>,
@@ -1279,7 +1277,6 @@ impl<'src> Compiler<'src> {
         let start = self.previous_position.0;
         let start_register = self.next_register();
 
-        self.advance()?;
         self.expect(Token::LeftParenthesis)?;
 
         while !self.allow(Token::RightParenthesis)? {
@@ -1447,7 +1444,7 @@ impl<'src> Compiler<'src> {
 
             function_compiler.advance()?;
 
-            function_compiler.self_name = Some(text.to_string());
+            function_compiler.self_name = Some(text.into());
 
             Some((text, position))
         } else {
@@ -1518,13 +1515,14 @@ impl<'src> Compiler<'src> {
         function_compiler.compile()?;
         function_compiler.expect(Token::RightBrace)?;
 
-        let function_end = function_compiler.previous_position.1;
-
         self.previous_token = function_compiler.previous_token;
         self.previous_position = function_compiler.previous_position;
         self.current_token = function_compiler.current_token;
         self.current_position = function_compiler.current_position;
 
+        self.lexer.skip_to(self.current_position.1);
+
+        let function_end = function_compiler.previous_position.1;
         let function =
             ConcreteValue::function(function_compiler.finish(None, value_parameters.clone()));
         let constant_index = self.push_or_get_constant(function);
@@ -1535,8 +1533,6 @@ impl<'src> Compiler<'src> {
             return_type,
         };
 
-        self.lexer.skip_to(function_end);
-
         if let Some((identifier, position)) = identifier_info {
             let (local_index, _) = self.declare_local(
                 identifier,
@@ -1614,8 +1610,12 @@ impl<'src> Compiler<'src> {
         };
         let start = self.current_position.0;
 
+        println!("{} {}", self.previous_token, self.current_token);
+
         self.advance()?;
 
+        println!("{} {}", self.previous_token, self.current_token);
+
         let mut argument_count = 0;
 
         while !self.allow(Token::RightParenthesis)? {
diff --git a/dust-lang/src/disassembler.rs b/dust-lang/src/disassembler.rs
index 02105a3..a73edcc 100644
--- a/dust-lang/src/disassembler.rs
+++ b/dust-lang/src/disassembler.rs
@@ -287,9 +287,27 @@ impl<'a> Disassembler<'a> {
 
     pub fn disassemble(mut self) -> String {
         let width = Disassembler::default_width();
-        let top_border = "┌".to_string() + &"─".repeat(width - 2) + "┐";
-        let section_border = "│".to_string() + &"┈".repeat(width - 2) + "│";
-        let bottom_border = "└".to_string() + &"─".repeat(width - 2) + "┘";
+        let top_border = {
+            let mut border = "┌".to_string();
+            border += &"─".repeat(width - 2);
+            border += "┐";
+
+            border
+        };
+        let section_border = {
+            let mut border = "│".to_string();
+            border += &"┈".repeat(width - 2);
+            border += "│";
+
+            border
+        };
+        let bottom_border = {
+            let mut border = "└".to_string();
+            border += &"─".repeat(width - 2);
+            border += "┘";
+
+            border
+        };
         let name_display = self
             .chunk
             .name()
diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs
index 4bea669..1edd043 100644
--- a/dust-lang/src/lib.rs
+++ b/dust-lang/src/lib.rs
@@ -55,7 +55,9 @@ pub use crate::optimize::{optimize_control_flow, optimize_set_local};
 pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
 pub use crate::scope::Scope;
 pub use crate::token::{write_token_list, Token, TokenKind, TokenOwned};
-pub use crate::value::{AbstractValue, ConcreteValue, RangeValue, Value, ValueError, ValueRef};
+pub use crate::value::{
+    AbstractValue, ConcreteValue, DustString, RangeValue, Value, ValueError, ValueRef,
+};
 pub use crate::vm::{run, Vm, VmError};
 
 use std::fmt::Display;
diff --git a/dust-lang/src/native_function/logic.rs b/dust-lang/src/native_function/logic.rs
index 1dae66d..d0c055d 100644
--- a/dust-lang/src/native_function/logic.rs
+++ b/dust-lang/src/native_function/logic.rs
@@ -59,7 +59,7 @@ pub fn to_string<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<
         string.push_str(&argument_string);
     }
 
-    Ok(Some(Value::Concrete(ConcreteValue::String(string))))
+    Ok(Some(Value::Concrete(ConcreteValue::string(string))))
 }
 
 pub fn read_line<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<Value>, VmError> {
@@ -81,7 +81,7 @@ pub fn read_line<'a>(vm: &'a Vm<'a>, instruction: Instruction) -> Result<Option<
         Ok(_) => {
             buffer = buffer.trim_end_matches('\n').to_string();
 
-            Ok(Some(Value::Concrete(ConcreteValue::String(buffer))))
+            Ok(Some(Value::Concrete(ConcreteValue::string(buffer))))
         }
         Err(error) => Err(VmError::NativeFunction(NativeFunctionError::Io {
             error: error.kind(),
diff --git a/dust-lang/src/value/concrete_value.rs b/dust-lang/src/value/concrete_value.rs
index 7951362..59a1a7e 100644
--- a/dust-lang/src/value/concrete_value.rs
+++ b/dust-lang/src/value/concrete_value.rs
@@ -1,11 +1,14 @@
 use std::fmt::{self, Display, Formatter};
 
 use serde::{Deserialize, Serialize};
+use smartstring::{LazyCompact, SmartString};
 
 use crate::{Chunk, Type, Value, ValueError, ValueRef};
 
 use super::RangeValue;
 
+pub type DustString = SmartString<LazyCompact>;
+
 #[derive(Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
 pub enum ConcreteValue {
     Boolean(bool),
@@ -16,7 +19,7 @@ pub enum ConcreteValue {
     Integer(i64),
     List(Vec<ConcreteValue>),
     Range(RangeValue),
-    String(String),
+    String(DustString),
 }
 
 impl ConcreteValue {
@@ -36,11 +39,11 @@ impl ConcreteValue {
         ConcreteValue::List(into_list.into())
     }
 
-    pub fn string<T: ToString>(to_string: T) -> Self {
-        ConcreteValue::String(to_string.to_string())
+    pub fn string<T: Into<SmartString<LazyCompact>>>(to_string: T) -> Self {
+        ConcreteValue::String(to_string.into())
     }
 
-    pub fn as_string(&self) -> Option<&String> {
+    pub fn as_string(&self) -> Option<&DustString> {
         if let ConcreteValue::String(string) = self {
             Some(string)
         } else {
@@ -91,33 +94,6 @@ impl ConcreteValue {
         Ok(sum)
     }
 
-    pub fn add_assign(&mut self, other: &Self) -> Result<(), ValueError> {
-        use ConcreteValue::*;
-
-        match (self, other) {
-            (Integer(left), Integer(right)) => {
-                *left += right;
-            }
-            (Float(left), Float(right)) => {
-                *left += right;
-            }
-            (String(left), String(right)) => {
-                *left += right;
-            }
-            (String(left), Character(right)) => {
-                *left += &right.to_string();
-            }
-            (left, right) => {
-                return Err(ValueError::CannotAdd(
-                    left.clone().to_value(),
-                    right.clone().to_value(),
-                ))
-            }
-        }
-
-        Ok(())
-    }
-
     pub fn subtract(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
         use ConcreteValue::*;
 
diff --git a/dust-lang/src/value/mod.rs b/dust-lang/src/value/mod.rs
index 08469b8..3472687 100644
--- a/dust-lang/src/value/mod.rs
+++ b/dust-lang/src/value/mod.rs
@@ -4,7 +4,7 @@ mod concrete_value;
 mod range_value;
 
 pub use abstract_value::AbstractValue;
-pub use concrete_value::ConcreteValue;
+pub use concrete_value::{ConcreteValue, DustString};
 pub use range_value::RangeValue;
 
 use std::fmt::{self, Debug, Display, Formatter};
diff --git a/dust-lang/tests/functions.rs b/dust-lang/tests/functions.rs
index 5002201..419361b 100644
--- a/dust-lang/tests/functions.rs
+++ b/dust-lang/tests/functions.rs
@@ -127,7 +127,7 @@ fn function_declaration() {
             ],
             vec![
                 ConcreteValue::function(Chunk::with_data(
-                    Some("add".to_string()),
+                    Some("add".into()),
                     FunctionType {
                         type_parameters: None,
                         value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]),
diff --git a/dust-lang/tests/math_add.rs b/dust-lang/tests/math_add.rs
index a95ea81..8006e3a 100644
--- a/dust-lang/tests/math_add.rs
+++ b/dust-lang/tests/math_add.rs
@@ -316,10 +316,7 @@ fn add_string_and_character() {
                 ),
                 (Instruction::r#return(true), Span(9, 9))
             ],
-            vec![
-                ConcreteValue::String("a".to_string()),
-                ConcreteValue::Character('b')
-            ],
+            vec![ConcreteValue::string("a"), ConcreteValue::Character('b')],
             vec![]
         ))
     );