Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Write idiomatic, robust, maintainable Rust.
SkillScore95/100
Test results 95
3/3 safety checks passed
73%95%of test cases passed +23 pts
Passed 21 of 22 test cases with the skill, and 16 of 22 without it.
5 fixed16 passed either way1 still failing0 broke
One test taskI have an internal assertion check: `assert!(slice.len() > 0)`. Is this assertion written according to best practices?
Adds every file the skill has, pinned so you can update it. No account needed.
pip install "decimalai>=0.15"
decimalai skills add morganlinton-rust-best-practices
claude --plugin-dir "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agents"The same install from Python, for a notebook or a setup script.
pip install "decimalai>=0.15"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("morganlinton-rust-best-practices")
if result.exit_code:
raise SystemExit(result.problems)
print("Added. Start Claude Code with the command below.")claude --plugin-dir "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agents"Type these in a Claude Code session. No account or API key. It writes the skill and any files it bundles, and asks first when its safety rating isn't Passed.
/plugin marketplace add decimal-labs/decimalai-claude-plugins
/plugin install decimalai@decimalai/decimalai:install morganlinton-rust-best-practicesThe plugin needs python3 and curl on your machine.
No Python or account. Claude Code picks it up in its next session.
curl -fsSL --create-dirs -o .claude/skills/morganlinton-rust-best-practices/SKILL.md https://app.decimal.ai/s/morganlinton-rust-best-practices/SKILL.md.claude/skills/morganlinton-rust-best-practices/SKILL.mdThis gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[claude-agent-sdk]"
decimalai skills add morganlinton-rust-best-practicesimport asyncio
from claude_agent_sdk import ClaudeAgentOptions
from decimalai.claude_agent_sdk import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
async def main():
question = "Hello!"
async with support.run(question) as run: # one user message, one run
async for message in run.query(options=ClaudeAgentOptions(max_turns=6)):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())Prefer its own loader? ClaudeAgentOptions(plugins=[skills_plugin()], skills="all") reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[claude-agent-sdk]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("morganlinton-rust-best-practices")
if result.exit_code:
raise SystemExit(result.problems)
import asyncio
from claude_agent_sdk import ClaudeAgentOptions
from decimalai.claude_agent_sdk import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
async def main():
question = "Hello!"
async with support.run(question) as run: # one user message, one run
async for message in run.query(options=ClaudeAgentOptions(max_turns=6)):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())Prefer its own loader? ClaudeAgentOptions(plugins=[skills_plugin()], skills="all") reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/morganlinton-rust-best-practices/SKILL.mdimport asyncio
from pathlib import Path
from claude_agent_sdk import ClaudeAgentOptions, query
skill = Path(".agents/skills/morganlinton-rust-best-practices/SKILL.md").read_text()
options = ClaudeAgentOptions(system_prompt=f"You are a helpful agent.\n\n{skill}", max_turns=6)
async def main():
async for message in query(prompt="Hello!", options=options):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())This gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai-agents]"
decimalai skills add morganlinton-rust-best-practicesfrom agents import Agent, Runner
from decimalai.openai_agents import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
name=support.name,
instructions=support.instructions, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")Using SandboxAgent? capabilities=[Shell(), decimalai.openai_agents.sandbox_skills()] points its own loader at the same folder.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai-agents]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("morganlinton-rust-best-practices")
if result.exit_code:
raise SystemExit(result.problems)
from agents import Agent, Runner
from decimalai.openai_agents import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
name=support.name,
instructions=support.instructions, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")Using SandboxAgent? capabilities=[Shell(), decimalai.openai_agents.sandbox_skills()] points its own loader at the same folder.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/morganlinton-rust-best-practices/SKILL.mdfrom pathlib import Path
from agents import Agent, Runner
skill = Path(".agents/skills/morganlinton-rust-best-practices/SKILL.md").read_text()
agent = Agent(name="support", instructions=f"You are a helpful agent.\n\n{skill}")
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")This gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai]"
decimalai skills add morganlinton-rust-best-practicesimport json
from openai import OpenAI
from decimalai.openai import agent_config
client = OpenAI()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your tools, as Chat Completions takes them, and what answers each.
TOOLS = [{"type": "function", "function": {
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
# run.system is your prompt, then the skill list.
messages = [{"role": "system", "content": run.system}, run.message]
while True:
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=messages,
tools=[*TOOLS, *run.tools], # your tools, then load_skill
).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
# load_skill is answered by the run; your own tools by FUNCTIONS.
function = FUNCTIONS.get(call.function.name)
messages.append(run.tool_result(call) or {
"role": "tool", "tool_call_id": call.id,
"content": function(**json.loads(call.function.arguments)) if function
else f"There is no tool named {call.function.name!r}.",
})
print(reply.content)
# The Responses API takes run.response_tools instead of run.tools.The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("morganlinton-rust-best-practices")
if result.exit_code:
raise SystemExit(result.problems)
import json
from openai import OpenAI
from decimalai.openai import agent_config
client = OpenAI()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your tools, as Chat Completions takes them, and what answers each.
TOOLS = [{"type": "function", "function": {
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
# run.system is your prompt, then the skill list.
messages = [{"role": "system", "content": run.system}, run.message]
while True:
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=messages,
tools=[*TOOLS, *run.tools], # your tools, then load_skill
).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
# load_skill is answered by the run; your own tools by FUNCTIONS.
function = FUNCTIONS.get(call.function.name)
messages.append(run.tool_result(call) or {
"role": "tool", "tool_call_id": call.id,
"content": function(**json.loads(call.function.arguments)) if function
else f"There is no tool named {call.function.name!r}.",
})
print(reply.content)
# The Responses API takes run.response_tools instead of run.tools.No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/morganlinton-rust-best-practices/SKILL.mdfrom pathlib import Path
from openai import OpenAI
client = OpenAI()
skill = Path(".agents/skills/morganlinton-rust-best-practices/SKILL.md").read_text()
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[
{"role": "system", "content": f"You are a helpful agent.\n\n{skill}"},
{"role": "user", "content": "Hello!"},
],
).choices[0].messageThis gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[anthropic]"
decimalai skills add morganlinton-rust-best-practicesimport anthropic
from decimalai.anthropic import agent_config
client = anthropic.Anthropic()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
question = "Hello!"
with support.run(question) as run: # one user message, one run
answer = client.beta.messages.tool_runner(
model="claude-haiku-5-5",
max_tokens=4096,
system=run.system, # your prompt, then the skill list
tools=[*run.tools], # your tools, then load_skill
messages=[{"role": "user", "content": question}],
).until_done()The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[anthropic]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("morganlinton-rust-best-practices")
if result.exit_code:
raise SystemExit(result.problems)
import anthropic
from decimalai.anthropic import agent_config
client = anthropic.Anthropic()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
question = "Hello!"
with support.run(question) as run: # one user message, one run
answer = client.beta.messages.tool_runner(
model="claude-haiku-5-5",
max_tokens=4096,
system=run.system, # your prompt, then the skill list
tools=[*run.tools], # your tools, then load_skill
messages=[{"role": "user", "content": question}],
).until_done()No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/morganlinton-rust-best-practices/SKILL.mdfrom pathlib import Path
import anthropic
client = anthropic.Anthropic()
skill = Path(".agents/skills/morganlinton-rust-best-practices/SKILL.md").read_text()
reply = client.messages.create(
model="claude-haiku-5-5",
max_tokens=4096,
system=f"You are a helpful agent.\n\n{skill}",
messages=[{"role": "user", "content": "Hello!"}],
)This gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[google-genai]"
decimalai skills add morganlinton-rust-best-practicesfrom google import genai
from google.genai import types
from decimalai.google_genai import agent_config
client = genai.Client()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your functions, as declarations for the model, and what answers each.
TOOLS = [{"function_declarations": [{
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters_json_schema": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}]}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
config = types.GenerateContentConfig(
system_instruction=run.system, # your prompt, then the skill list
tools=[*TOOLS, *run.tool_params], # your functions, then load_skill
)
contents = [{"role": "user", "parts": [{"text": run.message}]}]
while True:
response = client.models.generate_content(
model="gemini-3.5-flash", contents=contents, config=config,
)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
parts = []
for call in response.function_calls:
# load_skill is answered by the run; your own functions by FUNCTIONS.
# A loop of your own rather than automatic function calling, which
# raises KeyError when the model asks for a function you did not pass.
function = FUNCTIONS.get(call.name)
parts.append(run.tool_result(call) or {"function_response": {
"name": call.name, "id": call.id,
"response": {"result": function(**(call.args or {})) if function
else f"There is no function named {call.name!r}."},
}})
contents.append({"role": "user", "parts": parts})
print(response.text)The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[google-genai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("morganlinton-rust-best-practices")
if result.exit_code:
raise SystemExit(result.problems)
from google import genai
from google.genai import types
from decimalai.google_genai import agent_config
client = genai.Client()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your functions, as declarations for the model, and what answers each.
TOOLS = [{"function_declarations": [{
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters_json_schema": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}]}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
config = types.GenerateContentConfig(
system_instruction=run.system, # your prompt, then the skill list
tools=[*TOOLS, *run.tool_params], # your functions, then load_skill
)
contents = [{"role": "user", "parts": [{"text": run.message}]}]
while True:
response = client.models.generate_content(
model="gemini-3.5-flash", contents=contents, config=config,
)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
parts = []
for call in response.function_calls:
# load_skill is answered by the run; your own functions by FUNCTIONS.
# A loop of your own rather than automatic function calling, which
# raises KeyError when the model asks for a function you did not pass.
function = FUNCTIONS.get(call.name)
parts.append(run.tool_result(call) or {"function_response": {
"name": call.name, "id": call.id,
"response": {"result": function(**(call.args or {})) if function
else f"There is no function named {call.name!r}."},
}})
contents.append({"role": "user", "parts": parts})
print(response.text)No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/morganlinton-rust-best-practices/SKILL.mdfrom pathlib import Path
from google import genai
from google.genai import types
client = genai.Client()
skill = Path(".agents/skills/morganlinton-rust-best-practices/SKILL.md").read_text()
response = client.models.generate_content(
model="gemini-3.5-flash",
contents="Hello!",
config=types.GenerateContentConfig(system_instruction=f"You are a helpful agent.\n\n{skill}"),
)This gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[langchain]" langchain-openai
decimalai skills add morganlinton-rust-best-practicesfrom langchain.agents import create_agent
from decimalai.langchain import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = create_agent(
"openai:gpt-5.4-mini",
tools=[], # your tools; the middleware adds load_skill
middleware=[support.middleware], # your prompt, the skill list and load_skill
)
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[langchain]" langchain-openaifrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("morganlinton-rust-best-practices")
if result.exit_code:
raise SystemExit(result.problems)
from langchain.agents import create_agent
from decimalai.langchain import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = create_agent(
"openai:gpt-5.4-mini",
tools=[], # your tools; the middleware adds load_skill
middleware=[support.middleware], # your prompt, the skill list and load_skill
)
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/morganlinton-rust-best-practices/SKILL.mdfrom pathlib import Path
from langchain.agents import create_agent
skill = Path(".agents/skills/morganlinton-rust-best-practices/SKILL.md").read_text()
agent = create_agent("openai:gpt-5.4-mini", tools=[], system_prompt=f"You are a helpful agent.\n\n{skill}")
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})This gets SKILL.md only. The skill's 6 other files are not included.
CrewAI's own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" "crewai>=1.15.27"
decimalai skills add morganlinton-rust-best-practicesimport decimalai.crewai
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=decimalai.crewai.skills(), # the installed folder
tools=decimalai.crewai.skill_tools(), # reads a skill's files, runs its scripts
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()The same install from Python, for a notebook or a setup script. CrewAI's own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" "crewai>=1.15.27"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("morganlinton-rust-best-practices")
if result.exit_code:
raise SystemExit(result.problems)
import decimalai.crewai
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=decimalai.crewai.skills(), # the installed folder
tools=decimalai.crewai.skill_tools(), # reads a skill's files, runs its scripts
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()No DecimalAI package. CrewAI's own loader reads the folder, and nothing pins or updates it.
.agents/skills/morganlinton-rust-best-practices/SKILL.mdfrom pathlib import Path
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=[Path(".agents/skills")],
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()This gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[adk]"
decimalai skills add morganlinton-rust-best-practicesfrom decimalai.adk import DecimalAIPlugin, agent_config
from google.adk.agents import LlmAgent
from google.adk.apps import App
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = LlmAgent(
name="support", # ADK needs a Python identifier
model="gemini-3.5-flash",
instruction=support.instruction, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# DecimalAIPlugin makes each invocation of runners built from this App one trace.
app = App(name="support", root_agent=agent, plugins=[DecimalAIPlugin()])Prefer ADK's own loader? SkillToolset(skills=decimalai.adk.skills(), ...) reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[adk]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("morganlinton-rust-best-practices")
if result.exit_code:
raise SystemExit(result.problems)
from decimalai.adk import DecimalAIPlugin, agent_config
from google.adk.agents import LlmAgent
from google.adk.apps import App
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = LlmAgent(
name="support", # ADK needs a Python identifier
model="gemini-3.5-flash",
instruction=support.instruction, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# DecimalAIPlugin makes each invocation of runners built from this App one trace.
app = App(name="support", root_agent=agent, plugins=[DecimalAIPlugin()])Prefer ADK's own loader? SkillToolset(skills=decimalai.adk.skills(), ...) reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/morganlinton-rust-best-practices/SKILL.mdfrom pathlib import Path
from google.adk.agents import LlmAgent
skill = Path(".agents/skills/morganlinton-rust-best-practices/SKILL.md").read_text()
agent = LlmAgent(
name="support",
model="gemini-3.5-flash",
instruction=lambda context: f"You are a helpful agent.\n\n{skill}",
)This gets SKILL.md only. The skill's 6 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[pydantic-ai]"
decimalai skills add morganlinton-rust-best-practicesfrom pydantic_ai import Agent
from decimalai.pydantic_ai import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
"openai:gpt-4o-mini",
name=support.name,
tools=[], # your tools; the capability adds load_skill
capabilities=[support], # your prompt, the skill list and load_skill
)
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")On the Pydantic AI harness, decimalai.pydantic_ai_harness.skills(project) uses its own loader instead.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[pydantic-ai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("morganlinton-rust-best-practices")
if result.exit_code:
raise SystemExit(result.problems)
from pydantic_ai import Agent
from decimalai.pydantic_ai import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
"openai:gpt-4o-mini",
name=support.name,
tools=[], # your tools; the capability adds load_skill
capabilities=[support], # your prompt, the skill list and load_skill
)
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")On the Pydantic AI harness, decimalai.pydantic_ai_harness.skills(project) uses its own loader instead.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/morganlinton-rust-best-practices/SKILL.mdfrom pathlib import Path
from pydantic_ai import Agent
skill = Path(".agents/skills/morganlinton-rust-best-practices/SKILL.md").read_text()
agent = Agent("openai:gpt-4o-mini", instructions=f"You are a helpful agent.\n\n{skill}")
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")This gets SKILL.md only. The skill's 6 other files are not included.
Deep Agents' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" deepagents
decimalai skills add morganlinton-rust-best-practicesimport decimalai.deepagents
from deepagents import create_deep_agent
agent = create_deep_agent(model=model, **decimalai.deepagents.skills())
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The default backend reads files but has no shell. To run a skill's scripts, pass skills(backend=LocalShellBackend(...)).
The same install from Python, for a notebook or a setup script. Deep Agents' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" deepagentsfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("morganlinton-rust-best-practices")
if result.exit_code:
raise SystemExit(result.problems)
import decimalai.deepagents
from deepagents import create_deep_agent
agent = create_deep_agent(model=model, **decimalai.deepagents.skills())
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The default backend reads files but has no shell. To run a skill's scripts, pass skills(backend=LocalShellBackend(...)).
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/morganlinton-rust-best-practices/SKILL.mdfrom pathlib import Path
from deepagents import create_deep_agent
skill = Path(".agents/skills/morganlinton-rust-best-practices/SKILL.md").read_text()
agent = create_deep_agent(model=model, system_prompt=f"You are a helpful agent.\n\n{skill}")
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})This gets SKILL.md only. The skill's 6 other files are not included.
Its own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" agent-framework
decimalai skills add morganlinton-rust-best-practicesimport asyncio
from agent_framework import Agent
from decimalai.agent_framework import skills
agent = Agent(client=client, instructions="You are a helpful agent.", context_providers=[skills()])
asyncio.run(agent.run("Hello!"))It caches skills by default, so restart the process after decimalai skills update.
The same install from Python, for a notebook or a setup script. Its own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" agent-frameworkfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("morganlinton-rust-best-practices")
if result.exit_code:
raise SystemExit(result.problems)
import asyncio
from agent_framework import Agent
from decimalai.agent_framework import skills
agent = Agent(client=client, instructions="You are a helpful agent.", context_providers=[skills()])
asyncio.run(agent.run("Hello!"))It caches skills by default, so restart the process after decimalai skills update.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/morganlinton-rust-best-practices/SKILL.mdimport asyncio
from pathlib import Path
from agent_framework import Agent
skill = Path(".agents/skills/morganlinton-rust-best-practices/SKILL.md").read_text()
agent = Agent(client=client, instructions=f"You are a helpful agent.\n\n{skill}")
asyncio.run(agent.run("Hello!"))This gets SKILL.md only. The skill's 6 other files are not included.
Strands' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" strands-agents
decimalai skills add morganlinton-rust-best-practicesfrom decimalai.strands import skills
from strands import Agent
from strands.vended_tools import file_editor, shell
agent = Agent(
model=model, # always pass one: the default is Amazon Bedrock
system_prompt="You are a helpful agent.",
tools=[file_editor, shell],
plugins=[skills()],
)
agent("Hello!")Strands loads an agent's skills once, so build the agent again after decimalai skills update.
The same install from Python, for a notebook or a setup script. Strands' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" strands-agentsfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("morganlinton-rust-best-practices")
if result.exit_code:
raise SystemExit(result.problems)
from decimalai.strands import skills
from strands import Agent
from strands.vended_tools import file_editor, shell
agent = Agent(
model=model, # always pass one: the default is Amazon Bedrock
system_prompt="You are a helpful agent.",
tools=[file_editor, shell],
plugins=[skills()],
)
agent("Hello!")Strands loads an agent's skills once, so build the agent again after decimalai skills update.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/morganlinton-rust-best-practices/SKILL.mdfrom pathlib import Path
from strands import Agent
skill = Path(".agents/skills/morganlinton-rust-best-practices/SKILL.md").read_text()
agent = Agent(model=model, system_prompt=f"You are a helpful agent.\n\n{skill}")
agent("Hello!")This gets SKILL.md only. The skill's 6 other files are not included.
Adds every file the skill has, pinned so you can update it. Cursor, Copilot, Cline and Warp read this folder by themselves.
pip install "decimalai>=0.15"
decimalai skills add morganlinton-rust-best-practicesNo Python or account. Cursor, Copilot, Cline and Warp read this folder by themselves.
curl -fsSL --create-dirs -o .agents/skills/morganlinton-rust-best-practices/SKILL.md https://app.decimal.ai/s/morganlinton-rust-best-practices/SKILL.md.agents/skills/morganlinton-rust-best-practices/SKILL.mdThis gets SKILL.md only. The skill's 6 other files are not included.
Give its text to your model as instructions. No account or package needed.
This gets SKILL.md only. The skill's 6 other files are not included.
Raw files: /s/morganlinton-rust-best-practices/SKILL.md · /s/morganlinton-rust-best-practices.json
Fetch the file and give its text to your model as instructions. No account or package needed.
curl -fsSL https://app.decimal.ai/s/morganlinton-rust-best-practices/SKILL.mdcurl -fsSL https://app.decimal.ai/s/morganlinton-rust-best-practices.jsonThis gets SKILL.md only. The skill's 6 other files are not included.
You don't need a DecimalAI account.
Saved prompts and skills, pinned versions, and traces of this skill in your runs. DecimalAI does not run your agent.
Claude Code reads files from your project, so DecimalAI config does not apply there.
On Claude Agent SDK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On OpenAI Agents: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On OpenAI SDK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Anthropic: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Gemini API: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On LangChain: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On CrewAI: set DECIMAL_API_KEY and add decimalai.init(crewai=True) to record runs and skill use. The skill version stays in your repo.
On Google ADK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Pydantic AI: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Deep Agents: set DECIMAL_API_KEY and add decimalai.init(langchain=True) to record runs and skill use. The skill version stays in your repo.
On Microsoft Agent Framework: set DECIMAL_API_KEY and add decimalai.init(otel=True, agent_name="support") to record runs and skill use. The skill version stays in your repo.
On Strands: set DECIMAL_API_KEY and add decimalai.init(otel=True, agent_name="support") to record runs and skill use. The skill version stays in your repo.
Editors read files from your project, so DecimalAI config does not apply there.
Anywhere else: record a use with decimalai.log_skill_activation() inside a trace.
Distilled from five Rust style and design guides, with a digest of each in references/.
Where they disagree, this file says which to follow and why.
Result, resources with ownership and Drop, polymorphism with
traits and enums, and nullability with Option. Inheritance-flavored tricks (Deref to
a "base" struct, Manager/Factory types, throw_if_null helpers) are smells.String or &str rather than impl Into<String>
parameters, anyhow-style errors, Vec return values, no speculative generality.Run these and fix everything they report:
cargo fmt
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo doc --no-deps # libraries: catches broken doc links and missing docs
#[expect(clippy::name, reason = "...")] on the narrowest item. expect warns when
the lint stops firing, so stale suppressions get cleaned up. Use #[allow] only in
generated code or macro output.#![deny(warnings)] in source: new toolchains add warnings and break the
build. Enforce warnings-as-errors in CI instead, with cargo clippy -- -D warnings.Cargo.toml ([lints] or [workspace.lints]), not with
scattered crate attributes. If a project has none, suggest lints/lints.toml and
lints/clippy.toml from https://github.com/morganlinton/BeautifulRust; the suppression
lints in it need rust-version 1.81 or later.Prefer unique ownership. Reach for Rc/Arc only when shared ownership is part of
the design, and for RefCell/Mutex only when interior mutability is. They are not
failures, but they move checking from compile time to run time, so use them on purpose.
Don't clone to make the borrow checker stop complaining. A clone that exists only to end a borrow produces a silent copy whose later changes are lost. Try these first, in roughly this order:
let, or shorten a borrow with a block.&mut self with std::mem::take (needs Default),
std::mem::replace, or Option::take instead of cloning it.Vec, or Rc<RefCell<_>> with Weak back-pointers, as
a last resort. Avoid self-referential structs.// Move out of a &mut enum without cloning.
fn a_to_b(e: &mut MyEnum) {
if let MyEnum::A { name, x: 0 } = e {
*e = MyEnum::B { name: std::mem::take(name) };
}
}
Accept the most general borrowed type. &str not &String, &[T] not &Vec<T>,
&T not &Box<T>, Option<&T> not &Option<T>. Deref coercion then lets callers pass
owned values, borrows, or literals. Where you only read a path, string, or byte buffer,
impl AsRef<Path> / impl AsRef<str> / impl AsRef<[u8]> is fine on parameters (never
on struct fields; store PathBuf and String). Library code that only iterates should
take impl IntoIterator<Item = T>; application code may simply take &[T].
Take ownership only when you need it, and then take it by value. If a function stores
or consumes its argument, take T, not &T plus an internal .clone(). If it only
reads, take &T; if it mutates in place, &mut T. Return multiple values as a tuple or
struct, not through &mut out-parameters (reusing a caller-owned buffer, as
Read::read does, is the exception).
Prefer owned data in structs. A struct holding &'a T forces a lifetime onto every
container. Owning a String or Vec<u8> is usually simpler and rarely the bottleneck.
When a type does carry a lifetime, give it a descriptive name ('src, 'conn); '_ and
'a are fine on simple functions.
Freeze after setup. Build with let mut, then rebind let data = data; so later
code cannot mutate it by accident.
Keep Rc, Arc, Box, and RefCell out of public signatures. Take &T,
&mut T, or T and let callers decide how to own things. A service that must be shared
implements a cheap Clone by wrapping its state in Arc<Inner> internally.
| Item | Case | Notes |
|---|---|---|
| Types, traits, enum variants | UpperCamelCase | Acronyms are one word: Uuid, HttpClient |
| Functions, methods, modules, locals | snake_case | |
| Constants, statics | SCREAMING_SNAKE_CASE | |
| Type parameters | short, from the main bound | T, R: Read, W: Write |
| Lifetimes | short lowercase | descriptive when on a type |
get_ prefix on getters: fn len(&self), fn name(&self) -> &str,
fn name_mut(&mut self). Use bare get only when there is one obvious thing to get
(Cell::get). Related: set_foo(T), take_foo(), with_foo(|f| ..) for closure
access, and unsafe _unchecked siblings for checked accessors.as_ (free, borrow to borrow), to_
(expensive, borrow to owned or borrow to borrow), into_ (consumes self). Put mut
in type order: as_mut_slice, not as_slice_mut.new (primary), with_capacity / new_with_foo (variants), try_new
(fallible), from_foo (conversion; pair important ones with a From impl). I/O
resources use domain verbs: File::open, TcpStream::connect.iter / iter_mut / into_iter return types named Iter / IterMut / IntoIter;
keys() returns Keys.ParseBoolError, not BoolParseError.Service, Manager, Factory, Helper, Util. Name the thing
it is (Bookings, Dispatcher, FooBuilder). Don't repeat the module name in the
type (foo::Id, not foo::FooId). Avoid abbreviations an outsider wouldn't know.len(), implement is_empty().std, serde, alloc, never use-std or no-std.struct Miles(f64) and struct Kilometers(f64)
cannot be mixed, where a type alias can. Add #[repr(transparent)] when layout matters,
and derive or forward the traits you need (derive_more cuts the boilerplate). A
newtype that encodes an invariant (Month) validates in a fallible constructor
(TryFrom, FromStr, try_new) and never offers an infallible From from the raw type.bool parameters in public or shared signatures. report(true) is unreadable at
the call site and cannot grow a third mode. Use a two-variant enum
(Verbosity::Summary, Verbosity::Full) or two methods (report(),
report_verbose()): Widget::new(Size::Small, Shape::Round), not
Widget::new(true, false). The only exception is a private helper whose call sites sit
a few lines away. When refactoring, an existing bool parameter is one of the smells to
remove, not a detail to preserve.Option for absence, never sentinels such as -1 or "". Data-carrying enums
instead of "field X is only meaningful when Y is set".PathBuf / &Path for paths, Duration for spans,
NonZeroU32 internally only (keep plain integers at API boundaries).bitflags crate; an enum models exactly one choice.Downstream crates cannot add std traits to your types (orphan rule), so you must. Derive
whenever every field supports it: Debug (on every public type; hand-write it to redact
secrets), Clone (except unique resources and key material), Copy (only small
bitwise-copyable types, because the compiler copies silently), PartialEq / Eq,
PartialOrd / Ord (never ship PartialOrd without Ord when a total order exists),
Hash (consistent with Eq), Default. Implement Display for anything meant to be
read: error types, string-like wrappers. Add #[must_use] to pure functions and to
types, such as errors and lazy values, whose discarding is a bug.
Implement From / TryFrom, never Into / TryInto directly (blanket impls give you
those). Prefer from / into over as, which silently allows lossy casts. Implement
FromStr rather than an ad-hoc from_str method, FromIterator and Extend on
collections, and IntoIterator for both owned and borrowed forms. Implement Deref only
on smart pointers and owning collections (Vec<T> to [T]), never to fake inheritance.
Overload operators only for their mathematical meaning.
Don't put derivable trait bounds on struct definitions: #[derive(Clone)] struct Good<T>,
not struct Bad<T: Clone + Debug>. Bounds belong on the impl blocks that need them.
If you hand-write Ord, hand-write PartialEq, PartialOrd, and Hash to agree with it
(compare the same fields, ignore the same fields). A derived PartialEq next to a custom
Ord breaks the std contract and misbehaves in BTreeMap and sort/dedup. Say in the
docs which fields equality ignores.
new() even when Default exists, and make them agree (one delegates to the
other). Derive or implement Default so callers can write ..Default::default();
bag-of-options structs with public fields should always have it.new, with_a, with_b).
Beyond that, or when construction validates or has side effects, add a builder:
Foo::builder() returns FooBuilder, setters are named after fields (timeout(d),
not set_timeout) and infallible, and build() -> Result<Foo, Error> does all
validation including cross-field checks. Prefer non-consuming setters
(&mut self -> &mut Self) unless build must move a value out.Deposit::new(account, amount) rather than four &str / u64 parameters.Private by default. Fields stay private unless the struct is passive C-style data.
Widen to pub(crate) or pub(super) before pub. Making an item more public later is
non-breaking; the reverse is breaking, and every pub item is a promise.
Mark public enums and public-field structs that cross crate boundaries
#[non_exhaustive] unless the set is a stable ontology. When matching one, list the
variants you handle and add a wildcard arm.
Seal traits you don't want implemented downstream, and say so in the docs:
pub trait TheTrait: private::Sealed { /* ... */ }
mod private {
pub trait Sealed {}
impl Sealed for usize {}
}
Library code returns iterators, not a Vec collected only to return, behind impl Iterator<Item = T> (internal APIs) or a newtype (public APIs you must keep stable).
Expose each public item at exactly one path. Don't pub use a foreign crate's items
unless you are an umbrella crate. Avoid leaking third-party types into your public API;
feature-gate serde impls behind a feature named serde. If a dependency's type must
appear, re-export it so callers can name the matching version.
Mark a function const only if it will stay const. Don't expose ErrorKind enums
or other internals that lock in failure modes you consider private.
T: Trait, impl Trait, dyn Trait) or
as an extension trait (FooExt). Don't use traits merely to organize code. Put
essential functionality in inherent methods and have trait impls forward to them, so
callers need no import.T: Trait, impl Trait); dyn Trait,
ideally wrapped in your own struct (struct DynStore(Arc<dyn Store>)). Generics give
static dispatch and inlining; trait objects give heterogeneous collections and smaller
binaries. Don't nest generics on service-level types (Service<Backend> is fine;
Service<Backend<Store>> is not).Self outside the
receiver break it; exclude such methods with where Self: Sized.Iterator does from next.Database::check(params). Associated functions are for
construction.FnOnce if called once, FnMut if it
mutates, else Fn) and prefer Fn* bounds to bare fn pointers. Closure parameters go
last; don't take more than one.macro_rules! to proc macros, make macro input look like the output it generates, and
never let a macro change a type's kind or a function's signature.&String / &Vec<T> parameters, unwrap on fallible
paths, Rc<RefCell<_>> in signatures, get_ getters, bool parameters, + string
concatenation, #![deny(warnings)], error types without Error, nondeterministic
HashMap iteration in output.Err is the change callers want. Dropping an error variant, removing a derive, or
changing what a constructor takes is not; if a signature must change, show the before
and after in the change list and say why.Pick the mechanism by contract:
| Situation | Use |
|---|---|
Absence is normal (first(), checked_add) | Option<T> |
| The caller may need to know what went wrong | Result<T, E> |
| A bug or violated invariant inside your code | panic! / assert! / expect("why") |
Panics are not exceptions. A panic means "stop now". Never use one to report bad
input, never rely on catch_unwind for control flow (it does nothing under
panic = "abort" and can leave state inconsistent), and never catch a panic and continue
inside a library. Where a panicking convenience is justified, pair it with a fallible API
(v[i] / v.get(i), new / try_new) and document it under # Panics.
Every intentional panic carries a message with the relevant values:
assert!(buf.len() >= HEADER, "buffer too small: {} bytes", buf.len()),
expect("config is loaded before workers start"). Prefer expect("reason") to
unwrap(). Don't restate the condition as the message.
Pattern-match instead of check-then-unwrap: if let Some(v) = opt { .. } or
let Some(v) = opt else { return .. };, never if opt.is_some() { opt.unwrap() }.
Lock poisoning is fatal: .lock().expect("lock poisoned"). Don't recover with
into_inner.
Library error types (any E in a public Result):
std::error::Error (which requires Debug + Display), be
Send + Sync + 'static, and implement source() for wrapped causes. Never use (),
String, or Box<dyn Error> as a library's public error type.Display messages are lowercase, concise, and have no trailing punctuation:
"invalid header length".From<Upstream> so ? converts. Use map_err only for foreign errors or
to add context.thiserror generates this
well), or a struct per situation with a private kind plus is_xxx() and accessor
methods. Either way, don't make one crate-wide enum that lists every failure mode in
the crate, and don't expose details callers cannot act on.FromUtf8Error::into_bytes).anyhow::Error in a library's public API.Application error handling: anyhow or eyre with .context("opening config") is
the right tool. Use one app-level error type, not several. main may return
Result<(), anyhow::Error>.
Destructors never fail, panic, or block. Provide close(self) -> Result<()> for
fallible teardown and let Drop do best-effort cleanup.
Document failure modes: # Errors for any Result, # Panics for any reachable
panic, # Safety for every unsafe fn.
Iterators over hand loops when the pipeline is short and reads top-down:
values.iter().filter(..).map(..).collect(), .any(), .sum(), .fold(). Collect
fallible steps into Result<Vec<_>, E> and apply ?. Keep an explicit for loop when
the body is large, does several jobs, or needs awkward early returns. Bind a long chain
to a named variable rather than putting it in a for header, and prefer for x in it
to .for_each(|x| ..).
Option / Result combinators (map, and_then, ok_or_else, unwrap_or_else,
as_ref, as_deref) when they're clearer than match, and match when they're not.
format! / write! with inline args (format!("{name}: {count}")) for
concatenation and substitution, not chained + or str::replace. write! into an
existing buffer in hot paths.
Explicit types where inference obscures intent: on .into(), .parse(),
.collect(), and complex iterator results. Prefer let x: Vec<u32> = .. to a turbofish.
Drop guards for cleanup that must run on early return or unwind. Bind them to a
name (let _guard = ..), because let _ = .. drops immediately.
Closures that capture mixed ownership: rebind in a block, then move:
let job = {
let cfg = cfg.clone();
move || run(cfg, data)
};
Import style: one use per module path (use foo::bar::{Bar, Baz};); types and
traits imported and used unqualified (Regex); free functions qualified by their module
(mem::take, fs::read). Import a trait inside the function that needs it unless the
whole file uses it. No globs except use super::* in tests, use Enum::* in a function
that matches on it, and a framework's prelude such as use bevy::prelude::*.
Magic numbers become named consts with a doc comment saying where the value
comes from and what changing it affects.
Logging: structured events through tracing or log, not println! / dbg!,
except for a CLI's own stdout output.
Public types, and the futures they return, should be Send (and usually Sync). Pin
it with a compile-time check so a refactor can't silently regress it:
const _: () = {
const fn assert_send<T: Send>() {}
assert_send::<MyClient>();
};
Holding an Rc or a RefCell borrow across an .await makes a future !Send.
Prefer message passing (channels) to shared state. When sharing is necessary: keep data
with linked invariants under one lock, keep lock scopes to a few lines, never call user
closures while holding a lock, and never return a MutexGuard from a public method.
Write async fn rather than fn -> impl Future, except in traits or when setup must
run outside the async block for a hot path.
Long CPU-bound work inside async must yield (tokio::task::yield_now().await every
tens of microseconds) or move to spawn_blocking.
Library code doesn't spawn raw threads or assume a runtime; take an executor, a spawn
function, or impl AsyncRead / impl Read from the caller.
unsafe only for one of three reasons: a novel abstraction safe code can't
express, a benchmarked performance need, or FFI and platform calls. Reach for std and
well-reviewed crates first; they already contain the audited unsafe.unsafe block or impl gets a // SAFETY: comment stating why the invariants
hold. Every unsafe fn gets a # Safety doc section listing what the caller must
uphold. Enable unsafe_op_in_unsafe_fn.unsafe in the smallest module that can uphold its invariant and wrap it in a
safe API; the module boundary is the soundness boundary. A safe function that can cause
undefined behavior through any sequence of safe calls is unsound, and unsound code is
never acceptable.unsafe only when misuse can cause undefined behavior, not because it
is dangerous (delete_database() is a safe function).transmute to cast enums or dodge lifetimes, and no unsafe impl Send to silence
the compiler.#[repr(C)] on shared structs, sized integer types, CString / CStr for
strings, allocate and free on the same side, wrap C-owned pointers in an RAII type whose
Drop frees them, check for null and return Result, and stop panics at the boundary
with catch_unwind. Generate bindings with bindgen in a foo-sys crate and keep
business logic in a safe core crate.crates/ workspace; never nest a
crate inside another crate's src/. Split when a crate grows a second purpose or a
subdirectory. Share versions and lints through [workspace.dependencies] and
[workspace.lints].Foo lives in foo.rs).
Non-leaf modules contain only mod declarations and re-exports. Put the essential items
at the crate root and group the rest by use case (account, network), not by kind
(traits, errors). Don't define a prelude.pub use foo::{A, B};), never with a glob.build.rs and proc macros. Use ranges ("1", "1.4"), never "*" or a
needless "=". Commit Cargo.lock for binaries. Audit with cargo tree --duplicates,
cargo deny, cargo udeps, and cargo audit.std, serde, alloc, test-util. Never
no-std, use-serde, or a feature that removes or changes a public item. Don't
feature-gate struct fields or trait methods. Put mocks and fakes behind test-util.#[non_exhaustive], adding a field to a publicly
constructible struct, adding a trait bound, or making a trait non-object-safe.
Deprecate in a minor release before removing in a major one. Use the latest stable
edition for new crates and set an MSRV deliberately.main() (no ctor-style crates). Don't use unstable features
on a stable toolchain./// on every public item; //! at the top of every public module and the crate root.
The first sentence is a single line of about fifteen words, because it becomes the
summary in listings.# Examples, # Errors, # Panics,
# Safety. Examples use ? with the fn main hidden behind # lines, never
unwrap(), because users copy them verbatim. Describe parameters in prose, not a table./// Removes an element. on remove_element is noise.[`Type`]. Hide impls users never trigger
(From<PrivateError> for PublicError) with #[doc(hidden)].// comments explain why, not what: // SAFETY:, // TODO(issue-123):, non-obvious
magic values, workarounds with a link. No design journals, "why we chose X" essays, or
rule-compliance tables in code docs. Document the end state.#[cfg(test)] mod tests { use super::*; .. } for private internals.
Integration tests in tests/ for anything reachable through the public API, and prefer
that location when either would do. Doc examples are tests too.new_mocked() -> (Self, MockCtrl) behind test-util, not a Default that performs I/O.cargo fuzz. Benchmark with criterion or divan
and std::hint::black_box. Run Miri over unsafe.assert_eq!(got, want, "input {input:?}").Design for usability first and optimize after measuring. When you do: Vec::with_capacity
or collect() over push loops; reuse caller-owned buffers in hot APIs
(fn read_into(&mut self, buf: &mut Vec<u8>)); Box<[T]>, Box<str>, Arc<str> for
long-lived immutable sequences; shrink_to_fit on long-lived collections built without a
reservation; a faster hasher (foldhash, FxHash) only for trusted keys; batch work
instead of per-item locking; applications may set a #[global_allocator] such as
mimalloc. Derive Copy for small plain types and never for large ones.
Before returning Rust code, confirm:
cargo fmt, cargo clippy -- -D warnings, and cargo test are clean.unwrap() outside tests and main; every expect, assert, and panic has a
message with the relevant values.&str, &[T], impl AsRef<_>, or impl IntoIterator, not
&String or &Vec<T>.Debug and the applicable common traits; error types
implement Error + Display + Send + Sync + 'static.bool parameters in public signatures; no hand-written Ord without matching
PartialEq and Hash..clone() whose only job is to satisfy the borrow checker.unsafe block has a // SAFETY: comment; every unsafe fn has # Safety docs.# Errors / # Panics where relevant.Read these when you need the reasoning behind a rule or a complete checklist for a task. Each is a condensed digest of one source.
references/rust-api-guidelines.md (https://rust-lang.github.io/api-guidelines/):
designing a public API or crate; all 54 checklist items with conventions.references/effective-rust.md (https://www.lurklurk.org/effective-rust/): choosing
between alternatives: generics vs dyn, Result vs panic, iterators vs loops,
borrow-checker tactics, dependencies, CI.references/rust-design-patterns.md (https://rust-unofficial.github.io/patterns/):
idioms, patterns, and anti-patterns with code.references/microsoft-rust-guidelines.md (https://microsoft.github.io/rust-guidelines/):
error struct design, panic policy, unsafe policy, async, workspace layout, performance.references/google-rust-style-guide.md (https://google.github.io/styleguide/rustguide.html):
formatting, imports, module layout, accessor naming, application vs library.references/bevy.md (https://docs.rs/bevy/0.20.0): read first if the crate uses Bevy.Automatically, when a task matches. Use this skill whenever you write, review, refactor, debug, or explain Rust code: new crates, modules, functions, structs, traits, error types, async code, unsafe or FFI code, Cargo.toml, tests, or rustdoc. Trigger on any mention of Rust, cargo, crates, .rs files, the borrow checker, lifetimes, traits, clippy, or rustfmt, even when the user does not ask for "best practices" or "idiomatic" code. Synthesized from the Google Rust Style Guide, the Rust API
Published skills whose descriptions closely match this one, with their headline benchmark lift.
Skills that do similar work, with their headline benchmark lift.
From the same source and nearby in the catalogue.