diff --git a/src/dev.rs b/src/dev.rs
index 40382a8..e09996f 100644
--- a/src/dev.rs
+++ b/src/dev.rs
@@ -1,4 +1,4 @@
-use anyhow::Result;
+use anyhow::{Context, Result};
 use clap::Subcommand;
 
 mod check;
@@ -13,8 +13,11 @@ pub enum DevCommands {
 impl DevCommands {
     pub fn run(self) -> Result<()> {
         match self {
-            DevCommands::Init => init::init(),
+            DevCommands::Init => init::init().context(INIT_ERR),
             DevCommands::Check => check::check(),
         }
     }
 }
+
+const INIT_ERR: &str = "Initialization failed.
+After resolving the issue, delete the `rustlings` directory (if it was created) and try again";
diff --git a/src/dev/init.rs b/src/dev/init.rs
index 01cfd9f..d382136 100644
--- a/src/dev/init.rs
+++ b/src/dev/init.rs
@@ -1,5 +1,101 @@
-use anyhow::Result;
+use std::fs::{self, create_dir};
+
+use anyhow::{Context, Result};
+
+use crate::CURRENT_FORMAT_VERSION;
 
 pub fn init() -> Result<()> {
-    todo!()
+    create_dir("rustlings").context("Failed to create the directory `rustlings`")?;
+
+    create_dir("rustlings/exercises")
+        .context("Failed to create the directory `rustlings/exercises`")?;
+
+    create_dir("rustlings/solutions")
+        .context("Failed to create the directory `rustlings/solutions`")?;
+
+    fs::write(
+        "rustlings/info.toml",
+        format!("{INFO_FILE_BEFORE_FORMAT_VERSION}{CURRENT_FORMAT_VERSION}{INFO_FILE_AFTER_FORMAT_VERSION}"),
+    )
+    .context("Failed to create the file `rustlings/info.toml`")?;
+
+    fs::write(
+        "rustligns/Cargo.toml",
+        format!("{CARGO_TOML_COMMENT}{}", crate::init::CARGO_TOML_PACKAGE),
+    )
+    .context("Failed to create the file `rustlings/Cargo.toml`")?;
+
+    fs::write("rustlings/.gitignore", crate::init::GITIGNORE)
+        .context("Failed to create the file `rustlings/.gitignore`")?;
+
+    fs::write("rustlings/README.md", README)
+        .context("Failed to create the file `rustlings/README.md`")?;
+
+    create_dir("rustlings/.vscode")
+        .context("Failed to create the directory `rustligns/.vscode`")?;
+    fs::write(
+        "rustlings/.vscode/extensions.json",
+        crate::init::VS_CODE_EXTENSIONS_JSON,
+    )
+    .context("Failed to create the file `rustlings/.vscode/extensions.json`")?;
+
+    println!("{INIT_DONE}");
+
+    Ok(())
 }
+
+const INFO_FILE_BEFORE_FORMAT_VERSION: &str =
+    "# The format version is an indicator of the compatibility of third-party exercises with the
+# Rustlings program.
+# The format version is not the same as the version of the Rustlings program.
+# In case Rustlings makes an unavoidable breaking change to the expected format of third-party
+# exercises, you would need to raise this version and adapt to the new format.
+# Otherwise, the newest version of the Rustlings program won't be able to run these exercises.
+format_version = ";
+
+const INFO_FILE_AFTER_FORMAT_VERSION: &str = r#"
+
+# Optional multi-line message to be shown to users when just starting with the exercises.
+welcome_message = """Welcome to these third-party Rustlings exercises."""
+
+# Optional multi-line message to be shown to users after finishing all exercises.
+final_message = """We hope that you found the exercises helpful :D"""
+
+# Repeat this section for every exercise.
+[[exercises]]
+# Exercise name which is the exercise file name without the `.rs` extension.
+name = "???"
+
+# Optional directory name to be provided if you want to organize exercises in directories.
+# If `dir` is specified, the exercise path is `exercises/DIR/NAME.rs`
+# Otherwise, the path is `exercises/NAME.rs`
+# dir = "???"
+
+# A mutli-line hint to be shown to users on request.
+hint = """???"""
+"#;
+
+const CARGO_TOML_COMMENT: &str =
+    "# You shouldn't edit this file manually! It is updated by `rustlings dev check`
+
+";
+
+const README: &str = "# Rustlings 🦀
+
+Welcome to these third-party Rustlings exercises 😃
+
+First,
+[install Rustlings using the official instructions in the README of the Rustlings project](https://github.com/rust-lang/rustlings) ✅
+
+Then, open your terminal in this directory and run `rustlings` to get started with the exercises 🚀
+";
+
+const INIT_DONE: &str = r#"Initialization done!
+You can start developing third-party Rustlings exercises in the `rustlings` directory :D
+
+If the initialization was done in a Rust project which is a Cargo workspace, you need to add the
+path to the `rustlings` directory to the `workspace.exclude` list in the project's `Cargo.toml`
+file. For example:
+
+[workspace]
+exclude = ["rustlings"]"#;
diff --git a/src/init.rs b/src/init.rs
index 459519d..3202017 100644
--- a/src/init.rs
+++ b/src/init.rs
@@ -1,14 +1,14 @@
 use anyhow::{bail, Context, Result};
 use std::{
     env::set_current_dir,
-    fs::{create_dir, OpenOptions},
-    io::{self, ErrorKind, Write},
+    fs::{self, create_dir},
+    io::ErrorKind,
     path::Path,
 };
 
 use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo};
 
-fn create_cargo_toml(exercise_infos: &[ExerciseInfo]) -> io::Result<()> {
+fn cargo_toml(exercise_infos: &[ExerciseInfo]) -> Vec<u8> {
     let mut cargo_toml = Vec::with_capacity(1 << 13);
     cargo_toml.extend_from_slice(b"bin = [\n");
     for exercise_info in exercise_infos {
@@ -23,39 +23,10 @@ fn create_cargo_toml(exercise_infos: &[ExerciseInfo]) -> io::Result<()> {
         cargo_toml.extend_from_slice(b".rs\" },\n");
     }
 
-    cargo_toml.extend_from_slice(
-        br#"]
+    cargo_toml.extend_from_slice(b"]\n\n");
+    cargo_toml.extend_from_slice(CARGO_TOML_PACKAGE.as_bytes());
 
-[package]
-name = "rustlings"
-edition = "2021"
-publish = false
-"#,
-    );
-    OpenOptions::new()
-        .create_new(true)
-        .write(true)
-        .open("Cargo.toml")?
-        .write_all(&cargo_toml)
-}
-
-fn create_gitignore() -> io::Result<()> {
-    OpenOptions::new()
-        .create_new(true)
-        .write(true)
-        .open(".gitignore")?
-        .write_all(GITIGNORE)
-}
-
-fn create_vscode_dir() -> Result<()> {
-    create_dir(".vscode").context("Failed to create the directory `.vscode`")?;
-    OpenOptions::new()
-        .create_new(true)
-        .write(true)
-        .open(".vscode/extensions.json")?
-        .write_all(VS_CODE_EXTENSIONS_JSON)?;
-
-    Ok(())
+    cargo_toml
 }
 
 pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> {
@@ -78,21 +49,31 @@ pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> {
         .init_exercises_dir()
         .context("Failed to initialize the `rustlings/exercises` directory")?;
 
-    create_cargo_toml(exercise_infos)
+    fs::write("Cargo.toml", cargo_toml(exercise_infos))
         .context("Failed to create the file `rustlings/Cargo.toml`")?;
 
-    create_gitignore().context("Failed to create the file `rustlings/.gitignore`")?;
+    fs::write(".gitignore", GITIGNORE)
+        .context("Failed to create the file `rustlings/.gitignore`")?;
 
-    create_vscode_dir().context("Failed to create the file `rustlings/.vscode/extensions.json`")?;
+    create_dir(".vscode").context("Failed to create the directory `rustlings/.vscode`")?;
+    fs::write(".vscode/extensions.json", VS_CODE_EXTENSIONS_JSON)
+        .context("Failed to create the file `rustlings/.vscode/extensions.json`")?;
 
     Ok(())
 }
 
-const GITIGNORE: &[u8] = b"/target
-/.rustlings-state.txt
+pub const CARGO_TOML_PACKAGE: &str = r#"[package]
+name = "rustlings"
+edition = "2021"
+publish = false
+"#;
+
+pub const GITIGNORE: &[u8] = b"Cargo.lock
+.rustlings-state.txt
+target
 ";
 
-const VS_CODE_EXTENSIONS_JSON: &[u8] = br#"{"recommendations":["rust-lang.rust-analyzer"]}"#;
+pub const VS_CODE_EXTENSIONS_JSON: &[u8] = br#"{"recommendations":["rust-lang.rust-analyzer"]}"#;
 
 const PROBABLY_IN_RUSTLINGS_DIR_ERR: &str =
     "A directory with the name `exercises` and a file with the name `Cargo.toml` already exist